Design Patterns라는 책은 소프트웨어 커뮤니티에 디자인 패턴을 소개했습니다. 이 책의 저자는 Erich Gamma, Richard Helm, Ralph Johnson 및 John Vlissides Design(일반적으로 "Gang of Four"로 알려짐)입니다. 제시된 디자인 패턴의 핵심 개념은 매우 간단합니다. 수년간 소프트웨어 개발을 연습한 후 Gamma와 다른 사람들은 건축가가 집과 건물을 디자인하고 욕실이 있어야 할 위치나 주방을 어떻게 구성해야 하는지에 대한 템플릿을 개발하는 것과 마찬가지로 고정된 디자인의 특정 패턴을 발견했습니다. 이러한 템플릿 또는 디자인 패턴을 사용한다는 것은 더 나은 건물을 더 빠르게 디자인한다는 것을 의미합니다. 소프트웨어에도 동일한 개념이 적용됩니다.
디자인 패턴은 강력한 소프트웨어를 더 빠르게 개발하는 데 유용한 방법일 뿐만 아니라 대규모 아이디어를 친숙한 용어로 요약하는 방법도 제공합니다. 예를 들어, 느슨한 결합을 제공하는 메시징 시스템을 작성 중이거나 Observer라는 패턴을 작성 중이라고 말할 수 있습니다.
작은 예제로 패턴의 가치를 입증하는 것은 매우 어렵습니다. 패턴은 실제로 대규모 코드 기반에서 작동하기 때문에 이는 종종 과잉처럼 느껴집니다. 이 문서에서는 대규모 애플리케이션을 보여주지 않으므로 이 문서에서 설명하는 코드 자체가 아닌 자신의 대규모 애플리케이션에 예제의 원칙을 적용하는 방법에 대해 생각해야 합니다. 이는 소규모 애플리케이션에서 패턴을 사용해서는 안 된다는 의미는 아닙니다. 많은 좋은 응용 프로그램은 작은 응용 프로그램으로 시작하여 큰 응용 프로그램으로 진행되므로 이러한 견고한 코딩 방법을 기반으로 구축하지 않을 이유가 없습니다.
이제 디자인 패턴과 그 유용성에 대해 알았으니 PHP V5에서 일반적으로 사용되는 5가지 패턴을 살펴보겠습니다.
원래 Design Patterns 책에서는 많은 디자인 패턴이 느슨한 결합 사용을 권장합니다. 이 개념을 이해하려면 많은 개발자가 대규모 시스템에서 작업하면서 겪는 힘든 여정에 대해 이야기하는 것이 가장 좋습니다. 코드 한 부분이 변경되고 나머지 시스템이 변경되면 문제가 발생합니다. — 전혀 관련이 없다고 생각했던 부분에 연쇄 피해가 발생할 가능성이 있습니다.
문제는 단단한 결합입니다. 시스템의 한 부분에 있는 함수와 클래스는 시스템의 다른 부분에 있는 함수와 클래스의 동작과 구조에 크게 의존합니다. 이러한 클래스가 서로 통신할 수 있도록 하는 패턴 세트가 필요하지만 연동을 피하기 위해 클래스를 서로 단단히 묶고 싶지는 않습니다.
대규모 시스템에서는 많은 코드가 몇 가지 주요 클래스에 의존합니다. 이러한 클래스를 변경해야 할 때 어려움이 발생할 수 있습니다. 예를 들어, 파일에서 읽는 User 클래스가 있다고 가정합니다. 데이터베이스에서 읽는 다른 클래스로 변경하려고 하지만 모든 코드는 파일에서 읽는 원래 클래스를 참조합니다. 이때는 공장모드를 이용하시면 매우 편리합니다.
Factory Pattern은 객체를 생성하는 특정 메소드가 있는 클래스입니다. new를 직접 사용하는 대신 팩토리 클래스를 사용하여 객체를 생성할 수 있습니다. 이렇게 하면 생성된 객체의 유형을 변경하려면 팩토리만 변경하면 됩니다. 이 팩토리를 사용하는 모든 코드는 자동으로 변경됩니다.
목록 1은 팩토리 클래스의 예를 보여줍니다. 방정식의 서버 측은 피드백을 추가하고, 피드백 목록을 요청하고, 특정 피드백과 관련된 기사를 얻을 수 있는 데이터베이스와 PHP 페이지 세트라는 두 부분으로 구성됩니다.
<?php interface IUser { function getName(); } class User implements IUser { public function __construct( $id ) { } public function getName() { return "Jack"; } } class UserFactory { public static function Create( $id ) { return new User( $id ); } } $uo= UserFactory::Create( 1 ); echo( $uo->getName()."\n" ); ?> |
IUser 인터페이스는 사용자 개체가 수행해야 하는 작업을 정의합니다. IUser의 구현을 User라고 하며 UserFactory 팩토리 클래스는 IUser 객체를 생성합니다. 이 관계는 그림에 표시될 수 있습니다. 1의 UML 표현.
PHP 인터프리터를 사용하여 명령줄에서 이 코드를 실행하면 다음과 같은 결과를 얻게 됩니다.
% phpfactory1.php Jack % |
测试代码会向工厂请求 User 对象,并输出 getName 方法的结果。
有一种工厂模式的变体使用工厂方法。类中的这些公共静态方法构造该类型的对象。如果创建此类型的对象非常重要,此方法非常有用。例如,假设您需要先创建对象,然后设置许多属性。此版本的工厂模式会将该进程封装在单个位置中,这样,不用复制复杂的初始化代码,也不必将复制好的代码在在代码库中到处粘贴。
清单 2 显示使用工厂方法的一个示例。
<?php interface IUser { function getName(); } class User implements IUser { public static function Load( $id ) { return new User( $id ); } public static function Create( ) { return new User( null ); } public function __construct( $id ) { } public function getName() { return "Jack"; } } $uo= User::Load( 1 ); echo( $uo->getName()."\n" ); ?> |
这段代码要简单得多。它仅有一个接口 IUser 和一个实现此接口的 User 类。User 类有两个创建对象的静态方法。此关系可用图 2 中的 UML 表示。
在命令行中运行脚本产生的结果与清单 1 的结果相同,如下所示:
1 2 3 |
% php factory2.php Jack % |
如上所述,有时此类模式在规模较小的环境中似乎有些大材小用。不过,最好还是学习这种扎实的编码形式,以便应用于任意规模的项目中。
某些应用程序资源是独占的,因为有且只有一个此类型的资源。例如,通过数据库句柄到数据库的连接是独占的。您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销,在获取单个页面的过程中更是如此。
单元素模式可以满足此要求。如果应用程序每次包含且仅包含一个对象,那么这个对象就是一个单元素(Singleton)。清单 3 中的代码显示了 PHP V5 中的一个일반적인 PHP 디자인 패턴 공유。
<?php require_once("DB.php"); class DatabaseConnection { public static function get() { static $db= null; if ( $db == null ) $db= newDatabaseConnection(); return $db; } private $_handle= null; private function __construct() { $dsn= 'mysql://root:password@localhost/photos'; $this->_handle =& DB::Connect( $dsn, array() ); } public function handle() { return $this->_handle; } } print( "Handle = ".DatabaseConnection::get()->handle()."\n" ); print( "Handle = ".DatabaseConnection::get()->handle()."\n" ); ?> |
此代码显示名为 DatabaseConnection 的单个类。您不能创建自已的 DatabaseConnection,因为构造函数是专用的。但使用静态 get 方法,您可以获得且仅获得一个 DatabaseConnection 对象。此代码的 UML 如图 3 所示。
在两次调用间,handle 方法返回的数据库句柄是相同的,这就是最好的证明。您可以在命令行中运行代码来观察这一点。
% php singleton.php Handle = Object id #3 Handle = Object id #3 % |
返回的两个句柄是同一对象。如果您在整个应用程序中使用일반적인 PHP 디자인 패턴 공유,那么就可以在任何地方重用同一句柄。
您可以使用全局变量存储数据库句柄,但是,该方法仅适用于较小的应用程序。在较大的应用程序中,应避免使用全局变量,并使用对象和方法访问资源。
观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。
一个简单示例是系统中的用户列表。清单 4 中的代码显示一个用户列表,添加用户时,它将发送出一条消息。添加用户时,通过发送消息的日志观察者可以观察此列表。
<?php interface IObserver { function onChanged( $sender, $args ); } interface IObservable { function addObserver( $observer ); } class UserList implements IObservable { private $_observers= array(); public function addCustomer( $name ) { foreach( $this->_observers as $obs ) $obs->onChanged( $this, $name ); } public function addObserver( $observer ) { $this->_observers []= $observer; } } class UserListLogger implements IObserver { public function onChanged( $sender, $args ) { echo( "'$args' added to user list\n" ); } } $ul = new UserList(); $ul->addObserver( new UserListLogger() ); $ul->addCustomer( "Jack" ); ?> |
此代码定义四个元素:两个接口和两个类。IObservable 接口定义可以被观察的对象,UserList 实现该接口,以便将本身注册为可观察。IObserver 列表定义要通过怎样的方法才能成为观察者,UserListLogger 实现 IObserver 接口。图 4 的 UML 中展示了这些元素。
如果在命令行中运行它,您将看到以下输出:
% php observer.php 'Jack' added to user list % |
测试代码创建 UserList,并将 UserListLogger 观察者添加到其中。然后添加一个消费者,并将这一更改通知 UserListLogger。
认识到 UserList 不知道日志程序将执行什么操作很关键。可能存在一个或多个执行其他操作的侦听程序。例如,您可能有一个向新用户发送消息的观察者,欢迎新用户使用该系统。这种方法的价值在于 UserList 忽略所有依赖它的对象,它主要关注在列表更改时维护用户列表并发送消息这一工作。
此模式不限于内存中的对象。它是在较大的应用程序中使用的数据库驱动的消息查询系统的基础。
命令链 模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序,而不影响其他处理程序。清单 5 显示了此模式的一个示例。
<?php interface ICommand { function onCommand( $name, $args ); } class CommandChain { private $_commands= array(); public function addCommand( $cmd ) { $this->_commands []= $cmd; } public function runCommand( $name, $args ) { foreach( $this->_commands as $cmd ) { if ( $cmd->onCommand( $name, $args ) ) return; } } } class UserCommand implements ICommand { public function onCommand( $name, $args ) { if ( $name != 'addUser' ) return false; echo( "UserCommand handling 'addUser'\n" ); return true; } } class MailCommand implements ICommand { public function onCommand( $name, $args ) { if ( $name != 'mail' ) return false; echo( "MailCommand handling 'mail'\n" ); return true; } } $cc = new CommandChain(); $cc->addCommand( new UserCommand() ); $cc->addCommand( new MailCommand() ); $cc->runCommand( 'addUser', null ); $cc->runCommand( 'mail', null ); ?> |
이 코드는 ICommand 개체 목록을 유지 관리하는 CommandChain 클래스를 정의합니다. 두 클래스 모두 ICommand 인터페이스를 구현할 수 있습니다. - 하나는 이메일 요청에 응답하고, 다른 하나는 사용자 추가에 응답합니다. 그림 5는 UML을 보여줍니다.
일부 테스트 코드가 포함된 스크립트를 실행하면 다음과 같은 출력이 표시됩니다.
% php chain.php UserCommand 'addUser' MailCommand 처리 '메일' 처리 % |
代码首先创建 CommandChain 对象,并为它添加两个命令对象的实例。然后运行两个命令以查看谁对这些命令作出了响应。如果命令的名称匹配 UserCommand 或 MailCommand,则代码失败,不发生任何操作。
为处理请求而创建可扩展的架构时,命令链模式很有价值,使用它可以解决许多问题。
我们讲述的最后一个设计模式是策略 模式。在此模式中,算法是从复杂类提取的,因而可以方便地替换。例如,如果要更改搜索引擎中排列页的方法,则策略模式是一个不错的选择。思考一下搜索引擎的几个部分 —— 一部分遍历页面,一部分对每页排列,另一部分基于排列的结果排序。在复杂的示例中,这些部分都在同一个类中。通过使用策略模式,您可将排列部分放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。
作为一个较简单的示例,清单 6 显示了一个用户列表类,它提供了一个根据一组即插即用的策略查找一组用户的方法。
<?phpinterface IStrategy{ function filter( $record );} class FindAfterStrategy implements IStrategy{ private $_name; public function __construct( $name ) { $this->_name = $name; } public function filter( $record ) { return strcmp( $this->_name, $record ) <= 0; }} class RandomStrategy implements IStrategy{ public function filter( $record ) { return rand( 0, 1 ) >= 0.5; }} class UserList{ private $_list = array(); public function __construct( $names ) { if ( $names != null ) { foreach( $names as $name ) { $this->_list []= $name; } } } public function add( $name ) { $this->_list []= $name; } public function find( $filter ) { $recs = array(); foreach( $this->_list as $user ) { if ( $filter->filter( $user ) ) $recs []= $user; } return $recs; }} $ul = new UserList( array( "Andy", "Jack", "Lori", "Megan" ) );$f1 = $ul->find( new FindAfterStrategy( "J" ) );print_r( $f1 ); $f2 = $ul->find( new RandomStrategy() );print_r( $f2 );?>
此代码的 UML 如图 6 所示。
UserList 类是打包名称数组的一个包装器。它实现 find 方法,该方法利用几个策略之一来选择这些名称的子集。这些策略由 IStrategy 接口定义,该接口有两个实现:一个随机选择用户,另一个根据指定名称选择其后的所有名称。运行测试代码时,将得到以下输出:
% php strategy.phpArray( [0] => Jack [1] => Lori [2] => Megan)Array( [0] => Andy [1] => Megan)%
测试代码为两个策略运行同一用户列表,并显示结果。在第一种情况中,策略查找排列在 J 后的任何名称,所以您将得到 Jack、Lori 和 Megan。第二个策略随机选取名称,每次会产生不同的结果。在这种情况下,结果为 Andy 和 Megan。
策略模式非常适合复杂数据管理系统或数据处理系统,二者在数据筛选、搜索或处理的方式方面需要较高的灵活性。
本文介绍的仅仅是 PHP 应用程序中使用的几种最常见的设计模式。在设计模式 一书中演示了更多的设计模式。不要因架构的神秘性而放弃。模式是一种绝妙的理念,适用于任何编程语言、任何技能水平。
위 내용은 일반적인 PHP 디자인 패턴 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!