Heim > Artikel > Backend-Entwicklung > Gemeinsame Nutzung gängiger PHP-Entwurfsmuster
Das Buch „Design Patterns“ führte Designmuster in die Software-Community ein. Die Autoren des Buches sind Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides Design (allgemein bekannt als „Gang of Four“). Die Kernkonzepte hinter den vorgestellten Entwurfsmustern sind sehr einfach. Nach Jahren der Softwareentwicklung haben Gamma und andere bestimmte Muster mit festen Designs entdeckt, ähnlich wie Architekten Häuser und Gebäude entwerfen und Vorlagen dafür entwickeln, wo ein Badezimmer sein sollte oder wie eine Küche gebaut werden sollte. Die Verwendung dieser Vorlagen oder Entwurfsmuster bedeutet, dass bessere Gebäude schneller entworfen werden können. Das gleiche Konzept gilt für Software.
Entwurfsmuster stellen nicht nur eine nützliche Möglichkeit dar, robuste Software schneller zu entwickeln, sondern bieten auch eine Möglichkeit, große Ideen in freundliche Worte zu fassen. Sie könnten beispielsweise sagen, dass Sie ein Nachrichtensystem schreiben, das eine lose Kopplung bereitstellt, oder Sie könnten sagen, dass Sie ein Muster namens Observer schreiben.
Den Wert von Mustern anhand kleinerer Beispiele zu demonstrieren, ist sehr schwierig. Dies fühlt sich oft wie ein Overkill an, da Muster tatsächlich in großen Codebasen funktionieren. In diesem Artikel wird keine große Anwendung demonstriert, daher müssen Sie darüber nachdenken, wie Sie die Prinzipien des Beispiels in Ihrer eigenen großen Anwendung anwenden können – nicht den Code selbst, der in diesem Artikel demonstriert wird. Das bedeutet nicht, dass Sie in kleinen Anwendungen keine Muster verwenden sollten. Viele gute Anwendungen beginnen als kleine Anwendungen und entwickeln sich zu großen Anwendungen. Es gibt also keinen Grund, nicht auf solch solide Codierungspraktiken aufzubauen.
Da Sie nun über Entwurfsmuster und deren Nützlichkeit Bescheid wissen, werfen wir einen Blick auf die fünf häufig verwendeten Muster in PHP V5.
Ursprünglich im Buch „Design Patterns“ wird in vielen Entwurfsmustern die Verwendung einer losen Kopplung empfohlen. Um dieses Konzept zu verstehen, ist es am besten, über die beschwerliche Reise zu sprechen, die viele Entwickler bei der Arbeit an großen Systemen durchlaufen. Probleme treten auf, wenn ein Teil des Codes geändert wird und der Rest des Systems — Es ist möglich, dass kaskadierende Schäden an Teilen auftreten, von denen Sie dachten, dass sie überhaupt nichts miteinander zu tun haben.
Das Problem ist die enge Kopplung. Funktionen und Klassen in einem Teil des Systems hängen stark vom Verhalten und der Struktur von Funktionen und Klassen in anderen Teilen des Systems ab. Sie möchten eine Reihe von Mustern, die es diesen Klassen ermöglichen, miteinander zu kommunizieren, Sie möchten sie jedoch nicht eng miteinander verknüpfen, um eine gegenseitige Verzahnung zu vermeiden.
In großen Systemen hängt viel Code von einigen wenigen Schlüsselklassen ab. Wenn diese Klassen geändert werden müssen, kann es zu Schwierigkeiten kommen. Angenommen, Sie haben eine User-Klasse, die aus einer Datei liest. Sie möchten es in eine andere Klasse ändern, die aus der Datenbank liest, Ihr gesamter Code verweist jedoch auf die ursprüngliche Klasse, die aus der Datei liest. Zu diesem Zeitpunkt ist es sehr praktisch, den Werksmodus zu verwenden.
Factory Pattern ist eine Klasse mit bestimmten Methoden, die Objekte für Sie erstellen. Sie können Factory-Klassen verwenden, um Objekte zu erstellen, anstatt new direkt zu verwenden. Wenn Sie auf diese Weise den Typ des erstellten Objekts ändern möchten, müssen Sie nur die Fabrik ändern. Der gesamte Code, der diese Fabrik verwendet, wird automatisch geändert.
Listing 1 zeigt ein Beispiel einer Factory-Klasse. Die Serverseite der Gleichung besteht aus zwei Teilen: einer Datenbank und einer Reihe von PHP-Seiten, die es Ihnen ermöglichen, Feedback hinzuzufügen, eine Liste mit Feedback anzufordern und Artikel zu einem bestimmten Feedback zu erhalten.
<?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" ); ?> |
Die IUser-Schnittstelle definiert, welche Operationen das Benutzerobjekt ausführen soll. Die Implementierung von IUser heißt User, und die UserFactory-Factory-Klasse erstellt IUser-Objekte. Dieser Zusammenhang kann in der Abbildung dargestellt werden UML-Darstellung in 1.
Wenn Sie diesen Code in der Befehlszeile mit dem PHP-Interpreter ausführen, werden Sie dies tun Erhalten Sie die folgenden Ergebnisse:
|
测试代码会向工厂请求 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 中的一个Gemeinsame Nutzung gängiger PHP-Entwurfsmuster。
<?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 % |
返回的两个句柄是同一对象。如果您在整个应用程序中使用Gemeinsame Nutzung gängiger PHP-Entwurfsmuster,那么就可以在任何地方重用同一句柄。
您可以使用全局变量存储数据库句柄,但是,该方法仅适用于较小的应用程序。在较大的应用程序中,应避免使用全局变量,并使用对象和方法访问资源。
观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。
一个简单示例是系统中的用户列表。清单 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 ); ?> |
Dieser Code definiert die CommandChain-Klasse, die eine Liste von ICommand-Objekten verwaltet. Beide Klassen können die ICommand-Schnittstelle implementieren - Einer antwortet auf E-Mail-Anfragen und der andere auf das Hinzufügen von Benutzern. Abbildung 5 zeigt die UML.
Wenn Sie das Skript ausführen, das Testcode enthält, erhalten Sie die folgende Ausgabe:
% php chain.php UserCommand handling 'addUser' MailCommand handling 'mail' % |
代码首先创建 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 应用程序中使用的几种最常见的设计模式。在设计模式 一书中演示了更多的设计模式。不要因架构的神秘性而放弃。模式是一种绝妙的理念,适用于任何编程语言、任何技能水平。
Das obige ist der detaillierte Inhalt vonGemeinsame Nutzung gängiger PHP-Entwurfsmuster. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!