本文为你们介绍经常使用的三种php设计模式:单例模式、工厂模式、观察者模式,有须要的朋友能够参考下。php
1、首先来看,单例模式mysql
所谓单例模式,就是确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例,即在应用程序中只会有这个类的一个实例存在。
一般单例模式用在仅容许数据库访问对象的实例中,从而防止打开多个数据库链接,单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操做、显卡的驱动程序常被设计成单例。程序员
一个单例类应包括如下几点:
和普通类不一样,单例类不能被直接实例化,只能是由自身实例化。所以,要得到这样的限制效果,构造函数必须标记为private。
要让单例类不被直接实例化而能起到做用,就必须为其提供这样的一个实例。所以,就必需要让单例类拥有一个能保存类的实例的私有静态成员变量和对应的一个能访问到实例的公共静态方法。
在PHP中,为防止对单例类对象的克隆来打破单例类的上述实现形式,一般还为基提供一个空的私有__clone()方法。好吧,废话很少说,归纳以下sql
单例模式有如下3个特色:数据库
1.只能有一个实例,必须拥有一个构造函数,而且必须被标记为private编程
2.必须自行建立这个实例,拥有一个保存类的实例的静态成员变量设计模式
3.必须给其余对象提供这一实例,拥有一个访问这个实例的公共的静态方法缓存
单例类不能再其它类中直接实例化,只能被其自身实例化。它不会建立实例副本,而是会向单例类内部存储的实例返回一个引用安全
那么为何要使用PHP单例模式?框架
PHP一个主要应用场合就是应用程序与数据库打交道的场景,在一个应用中会存在大量的数据库操做,针对数据库句柄链接数据库的行为,使用单例模式能够避免大量的new操做。由于每一次new操做都会消耗系统和内存的资源。
在以往的项目开发中,没使用单例模式前的状况以下:
//初始化一个数据库句柄 $db = new DB(...); //好比有个应用场景是添加一条评论信息 $db->addComment(); ...... //若是咱们要在另外一地方使用这个评论信息,这时要用到数据库句柄资源,可能会这么作 ...... function comment() { $db = new DB(...); $db->getCommentInfo(); ...... //可能有些朋友也许会说,能够直接使用global关键字! global $db; ......
的确global能够解决问题,也起到单例模式的做用,但在OOP中,咱们建议拒绝这种编码。由于global存在安全隐患(全局变量不受保护的本质)。
全局变量是面向对象程序员遇到的引起BUG的主要缘由之一。这是由于全局变量将类捆绑于特定的环境,破坏了封装。若是新的应用程序没法保证一开始就定义了相同的全局变量,那么一个依赖于全局变量的类就没法从一个应用程序中提取出来并应用到新应用程序中。
确切的讲,单例模式偏偏是对全局变量的一种改进,避免那些存储惟一实例的全局变量污染命名空间。你没法用错误类型的数据覆写一个单例。这种保护在不支持命名空间的PHP版本里尤为重要。由于在PHP中命名冲突会在编译时被捕获,并使脚本中止运行。
咱们用单例模式改进下示例:
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 php echo $ob->getname();//good morning php
单例模式的优缺点:
优势:
1. 改进系统的设计
2. 是对全局变量的一种改进
缺点:
1. 难于调试
2. 隐藏的依赖关系
3. 没法用错误类型的数据覆写一个单例
2、工厂模式
工厂模式就是一种类,是指包含一个专门用来建立其余对象的方法的类,工厂类在多态性编程实践中是相当重要的,它容许动态的替换类,修改配置,一般会使应用程序更加灵活,熟练掌握工厂模式高级PHP开发人员是很重要的。
工厂模式一般用来返回符合相似接口的不一样的类,工厂的一种常见用法就是建立多态的提供者,从而容许咱们基于应用程序逻辑或者配置设置来决定应实例化哪个类,例如,可使用这样的提供者来扩展一个类,而不须要重构应用程序的其余部分,从而使用新的扩展后的名称 。
一般,工厂模式有一个关键的构造,根据通常原则命名为Factory的静态方法,然而这只是一种原则,工厂方法能够任意命名,这个静态还能够接受任意数据的参数,必须返回一个对象。
具备为您建立对象的某些方法,这样就可使用工厂类建立对象,工厂模式在于能够根据输入参数或者应用程序配置的不一样来建立一种专门用来实现化并返回其它类的实例的类,而不直接使用new,这样若是想更改建立的对象类型,只需更改该工厂便可,
先举个示例吧:
<?php class 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文件代码中都要进行修改。而使用工厂模式,只要在工厂方法或类里面进行修改便可。并且工厂模式是其余设计模式的基础。
对上面的简单工厂模式再进一步优化,好比:
利用工厂类生产对象:
<?php class 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 类代码发生变更,即:
<?php class 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 类也发生变更:
<?php class 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图:
3、观察者模式
观察者模式为您提供了避免组件之间紧密耦合的另外一种方法。该模式很是简单:观察者模式是一种事件系统,意味着这一模式容许某个类观察另外一个类的状态,当被观察的类状态发生改变的时候,观察类能够收到通知而且作出相应的动做;
<?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();