In diesem Artikel werden drei häufig verwendete PHP-Entwurfsmuster vorgestellt: Singleton-Modus, Factory-Modus und Beobachtermodus. Freunde in Not können sich darauf beziehen.
1. Schauen wir uns zunächst den Singleton-Modus an
Der sogenannte Singleton-Modus soll sicherstellen, dass es nur eine Instanz einer bestimmten Klasse gibt. und es instanziiert sich selbst und stellt es dem gesamten System zur Verfügung. Diese Instanz bedeutet, dass nur eine Instanz dieser Klasse in der Anwendung vorhanden ist.
Normalerweise wird der Singleton-Modus in Instanzen verwendet, die nur den Datenbankzugriff auf Objekte zulassen, wodurch verhindert wird, dass mehrere Datenbankverbindungen geöffnet werden. Der Singleton-Modus ist ein gängiges Entwurfsmuster in Computersystemen, Thread-Pools, Protokollobjekten und Dialogen Boxen, Drucker, Datenbankoperationen und Grafikkartentreiber werden oft als Singletons konzipiert.
Eine Singleton-Klasse sollte die folgenden Punkte enthalten:
Im Gegensatz zu gewöhnlichen Klassen können Singleton-Klassen nicht direkt, sondern nur für sich selbst instanziiert werden. Um solche restriktiven Effekte zu erzielen, muss der Konstruktor daher als privat gekennzeichnet sein.
Damit eine Singleton-Klasse funktioniert, ohne direkt instanziiert zu werden, muss eine solche Instanz dafür bereitgestellt werden. Daher ist es erforderlich, dass die Singleton-Klasse über eine private statische Mitgliedsvariable verfügt, die die Instanz der Klasse speichern kann, und über eine entsprechende öffentliche statische Methode, die auf die Instanz zugreifen kann.
Um zu verhindern, dass das Klonen des Singleton-Klassenobjekts die obige Implementierungsform der Singleton-Klasse beeinträchtigt, wird in PHP normalerweise eine leere private __clone()-Methode für die Basis bereitgestellt. Nun, ohne weitere Umschweife, die Zusammenfassung lautet wie folgt
Der Singleton-Modus hat die folgenden 3 Eigenschaften:
1. Es kann nur eine Instanz geben, sie muss einen Konstruktor haben und als privat markiert sein
2. Sie müssen diese Instanz selbst erstellen und über eine statische Mitgliedsvariable verfügen, die die Instanz der Klasse
3 speichert. Diese Instanz muss anderen Objekten bereitgestellt werden, und es gibt eine öffentliche statische Methode für den Zugriff auf diese Instanz
Eine Singleton-Klasse kann nicht direkt in anderen Klassen instanziiert werden, sondern nur von sich selbst instanziiert werden. Es wird keine Kopie der Instanz erstellt, sondern eine Referenz auf die in der Singleton-Klasse gespeicherte Instanz zurückgegeben.
Warum also das PHP-Singleton-Muster verwenden?
Eines der Hauptanwendungsszenarien von PHP ist das Szenario, in dem die Anwendung die Datenbank verarbeitet. In einer Anwendung gibt es eine große Anzahl von Datenbankoperationen für das Verhalten der Verbindung des Datenbank-Handles mit der Datenbank Durch die Verwendung des Singleton-Modus können viele neue Vorgänge vermieden werden. Denn jeder neue Vorgang verbraucht System- und Speicherressourcen.
In der vorherigen Projektentwicklung war die Situation vor der Verwendung des Singleton-Modus wie folgt:
//初始化一个数据库句柄$db = new DB(...);//比如有个应用场景是添加一条评论信息$db->addComment();......//如果我们要在另一地方使用这个评论信息,这时要用到数据库句柄资源,可能会这么做......function comment() { $db = new DB(...); $db->getCommentInfo();......//可能有些朋友也许会说,可以直接使用global关键字! global $db;......
Indeed Global kann das Problem auch lösen spielt die Rolle eines Singleton-Musters, aber in OOP empfehlen wir, diese Kodierung abzulehnen. Weil global Sicherheitsrisiken birgt (die ungeschützte Natur globaler Variablen).
Globale Variablen sind eine der Hauptursachen für Fehler, auf die objektorientierte Programmierer stoßen. Dies liegt daran, dass globale Variablen die Klasse an eine bestimmte Umgebung binden und so die Kapselung aufheben. Eine Klasse, die auf globalen Variablen basiert, kann nicht aus einer Anwendung extrahiert und auf eine neue Anwendung angewendet werden, wenn die neue Anwendung nicht garantieren kann, dass von Anfang an dieselben globalen Variablen definiert sind.
Um genau zu sein, ist der Singleton-Modus eine Verbesserung gegenüber globalen Variablen, die verhindert, dass globale Variablen, die eindeutige Instanzen speichern, den Namespace verschmutzen. Sie können einen Singleton nicht mit Daten des falschen Typs überschreiben. Dieser Schutz ist besonders wichtig in PHP-Versionen, die keine Namespaces unterstützen. Denn in PHP werden Namenskonflikte zur Kompilierungszeit abgefangen und führen dazu, dass das Skript nicht mehr ausgeführt wird.
Wir verwenden das Singleton-Muster, um das folgende Beispiel zu verbessern:
class Single { private $name;//声明一个私有的实例变量 private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。 } static public $instance;//声明一个静态变量(保存在类中唯一的一个实例) static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象 if(!self::$instance) self::$instance = new self(); return self::$instance; } public function setname($n){ $this->name = $n; } public function getname(){ return $this->name; } }$oa = Single::getinstance();$ob = Single::getinstance();$oa->setname('hello php world');$ob->setname('good morning php');echo $oa->getname();//good morning phpecho $ob->getname();//good morning php
Die Vor- und Nachteile des Singleton-Musters:
Vorteile:
1. Verbessern Sie das Design des Systems
Es ist eine Verbesserung gegenüber globalen Variablen
Nachteile:
1. Schwierig zu debuggen
2. Versteckte Abhängigkeiten
3. Ein Singleton kann nicht mit falschem Datentyp überschrieben werden
Das Factory-Muster ist eine Klasse, die eine Methode enthält, die speziell zum Erstellen anderer Objekte verwendet wird. Die Factory-Klasse ist in der Praxis der polymorphen Programmierung von entscheidender Bedeutung. Sie ermöglicht das dynamische Ersetzen von Klassen und die Änderung von Konfigurationen und macht die Anwendung normalerweise zum Programm ist flexibler und es ist wichtig, dass erfahrene PHP-Entwickler den Factory-Modus beherrschen.
Das Fabrikmuster wird normalerweise verwendet, um verschiedene Klassen zurückzugeben, die ähnlichen Schnittstellen entsprechen. Eine häufige Verwendung von Fabriken besteht darin, polymorphe Anbieter zu erstellen, sodass wir basierend auf Anwendungslogik oder Konfigurationseinstellungen entscheiden können, welche instanziiert werden sollen. Beispielsweise kann eine Klasse mithilfe eines solchen Anbieters erweitert werden, um den neuen erweiterten Namen zu verwenden, ohne dass andere Teile der Anwendung umgestaltet werden müssen. Normalerweise verfügt das Factory-Muster über eine statische Methode mit dem Namen Factory. Dies ist jedoch nur ein Prinzip. Diese statische Methode kann auch beliebig benannt werden Akzeptieren Sie Parameter aller Daten. Es muss ein Objekt zurückgegeben werden.具有为您创建对象的某些方法,这样就可以使用工厂类创建对象,工厂模式在于可以根据输入参数或者应用程序配置的不同来创建一种专门用来实现化并返回其它类的实例的类,而不直接使用new,这样如果想更改创建的对象类型,只需更改该工厂即可,
先举个示例吧:
<?phpclass Factory {//创建一个基本的工厂类 static public function fac($id){//创建一个返回对象实例的静态方法 if(1 == $id) return new A(); elseif(2==$id) return new B(); elseif(3==$id) return new C(); return new D(); } }interface FetchName {//创建一个接口 public function getname();//}class A implements FetchName{ private $name = "AAAAA"; public function getname(){ return $this->name; } }class C implements FetchName{ private $name = "CCCCC"; public function getname(){ return $this->name; } }class B implements FetchName{ private $name = "BBBBB"; public function getname(){ return $this->name; } }class D implements FetchName{ private $name = "DDDDD"; public function getname(){ return $this->name; } }$o = Factory::fac(6);//调用工厂类中的方法if($o instanceof FetchName){ echo $o->getname();//DDDDD}$p=Factory::fac(3);echo $p->getname();//CCCCC?>
个人意见,再说简单点吧,PHP工厂模式就是用一个工厂方法来替换掉直接new对象的操作,就是为方便扩展,方便使用,在新增实现基类中的类中方法时候,那么在工厂类中无需修改,传入参数可以直接使用,具体就是跳过工厂类修改,直接使用工厂类输出想要的结果。在传统习惯中,如果要生成一个类的话,在代码中直接new一个对象,比如:
class Database{ } $db = new Database();
下面介绍工厂模式的操作方法:
class Database{ } //创建一个工厂类class Factory { //创建一个静态方法 static function createDatabase(){ $db = new Database; return $db; } }
那么,当我们想创建一个数据库类的话,就可以使用这样的方法:
<?php $db = Factory::createDatabase();?>
简单工厂模式比直接new一个对象的好处是,比如Database这个类在很多php文件中都有使用到,当Database这个类发生了某些变更,比如修改了类名、或者一些参数发生了变化,那这时候如果你使用的是$db = new Database这种传统方法生成对象,那么在所有包含这种生成对象的php文件代码中都要进行修改。而使用工厂模式,只要在工厂方法或类里面进行修改即可。而且工厂模式是其他设计模式的基础。
对上面的简单工厂模式再进一步优化,比如:
利用工厂类生产对象:
<?phpclass Example { // The parameterized factory method public static function factory($type) { if (include_once 'Drivers/' . $type . '.php') { $classname = 'Driver_' . $type; return new $classname; } else { throw new Exception('Driver not found'); } } } // Load a MySQL Driver$mysql = Example::factory('MySQL'); // Load an SQLite Driver$sqlite = Example::factory('SQLite');?>
简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
要理解工厂模式这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分 —— 您曾认为完全不相关的部分中也有可能出现级联破坏。
该问题在于紧密耦合 。系统某个部分中的函数和类严重依赖于系统的其他部分中函数和类的行为和结构。您需要一组模式,使这些类能够相互通信,但不希望将它们紧密绑定在一起,以避免出现联锁。
在大型系统中,许多代码依赖于少数几个关键类。需要更改这些类时,可能会出现困难。例如,假设您有一个从文件读取的 User 类。您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。这时候,使用工厂模式会很方便。
看下实例:
<?php interface IUser { function getName(); } class User implements IUser { public $id; public function __construct( $id ) { } public function getName() { return "Fantasy"; } }?>
传统方法使用 User 类,一般都是这样:
<?php //在页面1$obj = new User(1); //在页面2$obj2 = new User(2); //在页面3$obj3 = new User(3);.... ?>
这时候,由于新的需求,使得User类要新增个参数或者User类名称发生变化,User 类代码发生变动,即:
<?phpclass User implements IUser { public $id,$pre; public function __construct( $id , $pre = '') {...} public function getName() { return $this->pre."Fantasy"; } }?>
接着,恐怖的事情发生了,假设之前有 100 个页面引用了之前的 User 类,那么这 100 个页面都要发生相应的改动:
//在页面1$obj = new User(1,'aaa'); //在页面2$obj = new User(2,'aaa'); //在页面3$obj = new User(3,'aaa');...
本来是一个小小的改动,但因紧密耦合的原因使得改动大吐血。而使用工厂模式则可以避免发生这种情况:
//User类为变动前class UserFactory { public static function Create( $id ) { return new User( $id ); } } //页面1$uo1 = UserFactory::Create( 1 ); //页面2$uo12 = UserFactory::Create( 2 );....
这时候需求变动,User 类也发生变动:
<?phpclass User implements IUser { public $id,$pre; public function __construct( $id , $pre = '') {...} public function getName() { return $this->pre."Jack"; } }?>
但是,我们不再需要去改动这 100 个页面,我们要改的仅仅是这个工厂类:
//class UserFactory { public static function Create( $id,$pre = 'aaa' ) { return new User( $id ,$pre); } }
其他100个页面不用做任何改动,这就是工厂设计模式带来的好处。看下UML图:
三、观察者模式
观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;
现在有两派,有的人建议使用设计模式,有的人不建议使用设计模式!
这就好比写文章一样,有的人喜欢文章按照套路走,比如叙事性质的文章,时间,地点,人物,事件。而有的人喜欢写杂文或者散文,有的人喜欢写诗词!
现在写代码很多地方类似于写文章,但是在有些地方比写文章需要更多的技能!写文章写多了一般也能写出优秀的文章,而代码也一样,写多了也能写出很多有写的代码!
很多时候,我看设计模式的时候,有些设计模式只是吻合我的代码习惯。但是你硬去套它,那么反而适得其反。——很多时候是学会了招式,在应用中不知不觉的使用上这些招式,才能掌握其道,但是也不要拘泥于招式,正所谓“无招胜有招”吗?
我学设计模式的初衷,就是知道有这么个玩意儿?脑子里有这么个印象,也不会生套它!如果设计模式不符合你的习惯对你阅读代码反而是不利的!
观察者模式定义对象的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新!
设计原则
在观察者模式中,会改变的是主题的状态以及观察者的数目。用这个模式,你可以改变依赖于主题状态的对象,却不必改变主题。——找出程序中会变化的方面,然后将其和固定不变的方面相分离!
主题和观察者都使用接口:观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者。这样可以让两者之间运作正常,又同时具有松耦合的优点! ——针对接口编程,不针对实现编程!
观察者模式利用“组合”将许多观察者组合进主题中。对象(观察者——主题)之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式产生的。 ——多用组合,少用继承!
好了,不说太多废话,直接上代码:
<?php /** * 观察者模式 * @author: Fantasy * @date: 2017/02/17 */ class Paper{ /* 主题 */ private $_observers = array(); public function register($sub){ /* 注册观察者 */ $this->_observers[] = $sub; } public function trigger(){ /* 外部统一访问 */ if(!empty($this->_observers)){ foreach($this->_observers as $observer) { $observer->update(); } } } } /** * 观察者要实现的接口 */interface Observerable { public function update(); } class Subscriber implements Observerable{ public function update() { echo "Callback\n"; } }?>
下面是测试代码:
/* 测试 */ $paper = new Paper(); $paper->register(new Subscriber()); //$paper->register(new Subscriber1()); //$paper->register(new Subscriber2());$paper->trigger();
总结
当新对象要填入的时候,只需要在主题(又叫可观察者)中进行注册(注册方式很多,你也可以在构造的时候,或者框架访问的接口中进行注册),然后实现代码直接在新对象的接口中进行。这降低了主题对象和观察者对象的耦合度。
好的设计模式不会直接进入你的代码中,而是进入你的大脑中。
更多PHP常用的三种设计模式 相关文章请关注PHP中文网!