thinkphp设计模式讲解

一、设计模式简介
首先我们来认识一下什么是设计模式:
设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。
设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式。
那么我们常说的架构、框架和设计模式有什么关系呢?
架构是一套体系结构,是项目的整体解决方案;框架是可供复用的半成品软件,是具体程序代码。架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代码里合理使用了很多设计模式。
二、提炼设计模式的几个原则:
开闭原则:模块应对扩展开放,而对修改关闭。
里氏代换原则:如果调用的是父类的话,那么换成子类也完全可以运行。
依赖倒转原则:抽象不依赖细节,面向接口编程,传递参数尽量引用层次高的类。
接口隔离原则:每一个接口只负责一种角色。
合成/聚合复用原则:要尽量使用合成/聚合,不要滥用继承。
三、设计模式的功用?
设计模式能解决
替换杂乱无章的代码,形成良好的代码风格
代码易读,工程师们都能很容易理解
增加新功能时不用修改接口,可扩展性强
稳定性好,一般不会出现未知的问题
设计模式不能解决:
设计模式是用来组织你的代码的模板,而不是直接调用的库;
设计模式并非最高效,但是代码的可读性和可维护性更重要;
不要一味追求并套用设计模式,重构时多考虑;

四、设计模式分类
1、创建型模式:
单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。
2、结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3、行为型模式:

模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

创建型模式:
单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。

1、单例模式
目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:数据库连接、缓存操作、分布式存储。

  1. php
  2. /**
  3.  *
  4.  * 单例模式
  5.  *
  6.  */
  7.      
  8. class DbConn
  9. {
  10.      
  11.        private static $_instance = null;
  12.        protected static $_counter = 0;
  13.        protected $_db;
  14.           
  15.        //私有化构造函数,不允许外部创建实例
  16.        private function __construct()
  17.        {
  18.               self::$_counter += 1;
  19.        }
  20.      
  21.        public function getInstance()
  22.        {
  23.               if (self::$_instance == null)
  24.               {
  25.                      self::$_instance = new DbConn();
  26.               }
  27.               return self::$_instance;
  28.        }
  29.      
  30.        public function connect()
  31.        {
  32.               echo "connected: ".(self::$_counter)."n";
  33.               return $this->_db;
  34.        }
  35.      
  36. }
  37.      
  38. /*
  39.  * 不使用单例模式时,删除构造函数的private后再测试,第二次调用构造函数后,_counter变成2
  40. */
  41. // $conn = new DbConn();
  42. // $conn->connect();
  43. // $conn = new DbConn();
  44. // $conn->connect();
  45.      
  46. //使用单例模式后不能直接new对象,必须调用getInstance获取
  47. $conn = DbConn::getInstance();
  48. $db = $conn->connect();
  49. //第二次调用是同一个实例,_counter还是1
  50. $conn = DbConn::getInstance();
  51. $db = $conn->connect();
  52. ?>
复制代码
特别说明:这里getInstance里有if判断然后再生成对象,在多线程语言里是会有并发问题的。例如java的解决方案有二个,给方法加上synchronized关键词变成同步,或者把_instanc的初始化提前放到类成员变量定义时,但是这2种方式php都不支持。不过因为php不支持多线程所以不需要考虑这个问题了。

2、工厂模式
实现:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
应用场景:众多子类并且会扩充、创建方法比较复杂。
  1. php
  2. /**
  3.  * 工厂模式
  4.  *
  5.  */
  6.      
  7. //抽象产品
  8. interface Person {
  9.     public function getName(); 
  10. }
  11. //具体产品实现
  12. class Teacher implements Person {
  13.     function getName() {
  14.         return "老师n";
  15.     }
  16. }
  17. class Student implements Person {
  18.     function getName() {
  19.         return "学生n";
  20.     }
  21. }
  22.      
  23. //简单工厂
  24. class SimpleFactory {
  25.        public static function getPerson($type) {
  26.               $person = null;
  27.               if ($type == 'teacher') {
  28.                      $person = new Teacher();
  29.               } elseif ($type == 'student') {
  30.                      $person = new Student();
  31.               }
  32.               return $person;
  33.        }
  34. }
  35.      
  36. //简单工厂调用
  37. class SimpleClient {
  38.        function main() {
  39.               // 如果不用工厂模式,则需要提前指定具体类
  40.               // $person = new Teacher();
  41.               // echo $person->getName();
  42.               // $person = new Student();
  43.               // echo $person->getName();
  44.      
  45.               // 用工厂模式,则不需要知道对象由什么类产生,交给工厂去决定
  46.               $person = SimpleFactory::getPerson('teacher');
  47.               echo $person->getName();
  48.               $person = SimpleFactory::getPerson('student');
  49.               echo $person->getName();
  50.        }
  51. }
  52.      
  53.      
  54. //工厂方法
  55. interface CommFactory {
  56.     public function getPerson();
  57. }
  58. //具体工厂实现
  59. class StudentFactory implements CommFactory {
  60.     function getPerson(){
  61.         return new Student();
  62.     }
  63. }
  64. class TeacherFactory implements CommFactory {
  65.     function getPerson() {
  66.         return new Teacher();
  67.     }
  68. }
  69.      
  70. //工厂方法调用
  71. class CommClient {
  72.     static function main() {
  73.            $factory = new TeacherFactory();
  74.            echo $factory->getPerson()->getName();
  75.            $factory = new StudentFactory();
  76.            echo $factory->getPerson()->getName();
  77.     }
  78. }
  79.      
  80.      
  81.      
  82. //抽象工厂模式另一条产品线
  83. interface Grade {
  84.        function getYear();
  85. }
  86. //另一条产品线的具体产品
  87. class Grade1 implements Grade {
  88.        public function getYear() {
  89.               return '2003级';
  90.        }
  91. }
  92. class Grade2 implements Grade {
  93.        public function getYear() {
  94.               return '2004级';
  95.        }
  96. }
  97. //抽象工厂
  98. interface AbstractFactory {
  99.        function getPerson();
  100.        function getGrade();
  101. }
  102. //具体工厂可以产生每个产品线的产品
  103. class Grade1TeacherFactory implements AbstractFactory {
  104.        public function getPerson() {
  105.               return new Teacher();
  106.        }
  107.        public function getGrade() {
  108.               return new Grade1();
  109.        }
  110. }
  111. class Grade1StudentFactory implements AbstractFactory {
  112.        public function getPerson() {
  113.               return new Student();
  114.        }
  115.        public function getGrade() {
  116.               return new Grade1();
  117.        }
  118. }
  119. class Grade2TeacherFactory implements AbstractFactory {
  120.        public function getPerson() {
  121.               return new Teacher();
  122.        }
  123.        public function getGrade() {
  124.               return new Grade2();
  125.        }
  126. }
  127. //抽象工厂调用
  128. class FactoryClient {
  129.        function printInfo($factory) {
  130.               echo $factory->getGrade()->getYear().$factory->getPerson()->getName();
  131.        }
  132.        function main() {
  133.               $client = new FactoryClient();
  134.               $factory = new Grade1TeacherFactory();
  135.               $client->printInfo($factory);
  136.               $factory = new Grade1StudentFactory();
  137.               $client->printInfo($factory);
  138.               $factory = new Grade2TeacherFactory();
  139.               $client->printInfo($factory);
  140.        }
  141. }
  142.      
  143.      
  144. //简单工厂
  145. //SimpleClient::main();
  146. //工厂方法
  147. //CommClient::main();
  148. //抽象工厂
  149. FactoryClient::main();
  150.      
  151. ?>
复制代码
三种工厂的区别是,抽象工厂由多条产品线,而工厂方法只有一条产品线,是抽象工厂的简化。而工厂方法和简单工厂相对,大家初看起来好像工厂方法增加了许多代码但是实现的功能和简单工厂一样。但本质是,简单工厂并未严格遵循设计模式的开闭原则,当需要增加新产品时也需要修改工厂代码。但是工厂方法则严格遵守开闭原则,模式只负责抽象工厂接口,具体工厂交给客户去扩展。在分工时,核心工程师负责抽象工厂和抽象产品的定义,业务工程师负责具体工厂和具体产品的实现。只要抽象层设计的好,框架就是非常稳定的。

3、创建者模式
在创建者模式中,客户端不再负责对象的创建与组装,而是把这个对象创建的责任交给其具体的创建者类,把组装的责任交给组装类,客户端支付对对象的调用,从而明确了各个类的职责。
应用场景:创建非常复杂,分步骤组装起来。
  1. php
  2. /**
  3.  * 创建者模式
  4.  */
  5.      
  6. //购物车
  7. class ShoppingCart {
  8.        //选中的商品
  9.     private $_goods = array();
  10.     //使用的优惠券
  11.     private $_tickets = array();
  12.      
  13.        public function addGoods($goods) {
  14.               $this->_goods[] = $goods;
  15.        }
  16.        
  17.     public function addTicket($ticket) {
  18.            $this->_tickets[] = $ticket;
  19.     }
  20.      
  21.     public function printInfo() {
  22.            printf("goods:%s, tickets:%sn", implode(',', $this->_goods), implode(',', $this->_tickets));
  23.     }
  24. }
  25.      
  26. //假如我们要还原购物车的东西,比如用户关闭浏览器后再打开时会根据cookie还原
  27. $data = array(
  28.        'goods' => array('衣服', '鞋子'),
  29.        'tickets' => array('减10'),
  30. );
  31.      
  32. //如果不使用创建者模式,则需要业务类里一步步还原购物车
  33. // $cart = new ShoppingCart();
  34. // foreach ($data['goods'] as $goods) {
  35. //   $cart->addGoods($goods);
  36. // }
  37. // foreach ($data['tickets'] as $ticket) {
  38. //   $cart->addTicket($ticket);
  39. // }
  40. // $cart->printInfo();
  41. // exit;
  42.      
  43.      
  44. //我们提供创建者类来封装购物车的数据组装
  45. class CardBuilder {
  46.        private $_card;
  47.        function __construct($card) {
  48.               $this->_card = $card;
  49.        }
  50.        function build($data) {
  51.               foreach ($data['goods'] as $goods) {
  52.                      $this->_card->addGoods($goods);
  53.               }
  54.               foreach ($data['tickets'] as $ticket) {
  55.                      $this->_card->addTicket($ticket);
  56.               }
  57.        }
  58.        function getCrad() {
  59.               return $this->_card;
  60.        }
  61. }
  62.      
  63. $cart = new ShoppingCart();
  64. $builder = new CardBuilder($cart);
  65. $builder->build($data);
  66. echo "after builder:n";
  67. $cart->printInfo();
  68.      
  69. ?>
复制代码
可以看出,使用创建者模式对内部数据复杂的对象封装数据组装过程后,对外接口就会非常简单和规范,增加修改新数据项也不会对外部造成任何影响。
3、 原型模式
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
应用场景: 类的资源非常多、性能和安全要求,一般和工厂方法结合使用。
  1. php
  2. /**
  3.  * 原型模式
  4.  */
  5.      
  6. //声明一个克隆自身的接口
  7. interface Prototype {
  8.     function copy(); 
  9. }   
  10.      
  11. //产品要实现克隆自身的操作
  12. class Student implements Prototype {
  13.        //简单起见,这里没有使用get set
  14.     public $school;
  15.     public $major;
  16.        public $name;
  17.      
  18.        public function __construct($school, $major, $name) {
  19.               $this->school = $school;
  20.               $this->major = $major;
  21.               $this->name = $name;
  22.        }
  23.      
  24.        public function printInfo() {
  25.               printf("%s,%s,%sn", $this->school, $this->major, $this->name);
  26.        }
  27.        
  28.     public function copy() {
  29.            return clone $this;
  30.     }
  31. }
  32.      
  33. $stu1 = new Student('清华大学', '计算机', '张三');
  34. $stu1->printInfo();
  35.      
  36. $stu2 = $stu1->copy();
  37. $stu2->name = '李四';
  38. $stu2->printInfo();
  39.      
  40. ?>
复制代码
这里可以看到,如果类的成员变量非常多,如果由外部创建多个新对象再一个个赋值,则效率不高代码冗余也容易出错,通过原型拷贝复制自身再进行微小修改就是另一个新对象了。
创建型模式就总结完了。下面还有两部分结构型设计模式和行为型设计模式稍后继续。

一、什么是结构型模式?
结构型模式是解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题。

二、结构型模式的种类:
适配器模式
桥接模式
装饰模式
组合模式
外观模式
享元模式
代理模式

1、 适配器模式(Adapter)
将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库。

代码实现

  1. /**    
  2.  * 适配器模式    
  3.  */
  4.           
  5. //老的代码     
  6. class User {    
  7.     private $name;    
  8.     function __construct($name) {    
  9.         $this->name = $name;    
  10.     }    
  11.     public function getName() {    
  12.         return $this->name;    
  13.     }    
  14. }    
  15.           
  16. //新代码,开放平台标准接口    
  17. interface UserInterface {    
  18.     function getUserName();    
  19. }    
  20. class UserInfo implements UserInterface {    
  21.     protected $user;    
  22.     function __construct($user) {    
  23.         $this->user = $user;    
  24.     }    
  25.     public function getUserName() {    
  26.         return $this->user->getName();    
  27.     }    
  28. }    
  29.             
  30. $olduser = new User('张三');    
  31. echo $olduser->getName()."n";    
  32. $newuser = new UserInfo($olduser);    
  33. echo $newuser->getUserName()."n";    
复制代码
注意点:这里的新接口使用了组合方式,UserInfo内部有一个成员变量保存老接口User对象,模块之间是松耦合的,这种结构其实就是组合模式。不要使用继承,虽然UserInfo继承User也能达到同样的目的,但是耦合度高,相互产生影响。


2、 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立变化
特点:独立存在,扩展性强
应用:需要不断更换调用对象却执行相同的调用方法,实现扩展功能

代码实现
  1. /**    
  2.  *    
  3.  * 桥接模式    
  4.  *     
  5.  */
  6.               
  7. abstract class Person {    
  8.     abstract function getJob();    
  9. }    
  10.               
  11. class Student extends Person {    
  12.     public function getJob() {    
  13.         return '学生';    
  14.     }    
  15. }    
  16.               
  17. class Teacher extends Person {    
  18.     public function getJob() {    
  19.         return '老师';    
  20.     }    
  21. }    
  22.               
  23. class BridgeObj {    
  24.     protected $_person;    
  25.               
  26.     public function setPerson($person) {    
  27.         $this->_person = $person;    
  28.     }    
  29.               
  30.     public function getJob() {    
  31.         return $this->_person->getJob();    
  32.     }    
  33. }    
  34.                   
  35. $obj = new BridgeObj();    
  36. $obj->setPerson(new Student());    
  37. printf("本次桥接对象:%sn", $obj->getJob());    
  38. $obj->setPerson(new Teacher());    
  39. printf("本次桥接对象:%sn", $obj->getJob());
复制代码
3、 装饰模式
动态地给一个对象添加额外的职责。在原有的基础上进行功能增强。
特点:用来增强原有对象功能,依附于原有对象。
应用:用于需要对原有对象增加功能而不是完全覆盖的时候

代码实现
  1. /**   
  2.  *     
  3.  * 装饰模式     
  4.  *     
  5.  */
  6.              
  7. //产品     
  8. abstract class Person {     
  9.     abstract function getPermission();     
  10. }     
  11. //被装饰者     
  12. class User extends Person {     
  13.     public function getPermission() {     
  14.         return '公开文档';     
  15.     }     
  16. }     
  17. //装饰类     
  18. class PermUser extends Person {     
  19.     protected $_user;     
  20.     protected $_special = '';     
  21.     function __construct($user) {     
  22.         $this->_user = $user;     
  23.     }     
  24.     public function getPermission() {     
  25.         return $this->_user->getPermission() . $this->_special;     
  26.     }     
  27. }     
  28. //装饰类产品     
  29. class JavaUser extends PermUser {     
  30.     protected $_special = ' java程序';     
  31. }     
  32. class CPlusUser extends PermUser {     
  33.     protected $_special = ' c++程序';     
  34. }     
  35.              
  36.              
  37. $user = new User();     
  38. printf("permission:%sn", $user->getPermission());     
  39. $user = new JavaUser($user);     
  40. printf("permission:%sn", $user->getPermission());     
  41. $user = new CPlusUser($user);     
  42. printf("permission:%sn", $user->getPermission());     
复制代码
大家想想装饰和继承的区别在哪?
如果是上面的例子,如果用继承,是CPlusUser继承JavaUser还是反过来呢?谁也不知道最终使用者需要哪一种。
在多层关系的情况下,装饰是和顺序无关并且随时增加装饰,而继承只能是特定的顺序,所以装饰模式会更加的灵活。

4、组合模式
将对象组合成树形结构表示“部分-整体”的层次结构。
特点:灵活性强
应用:对象的部分-整体的层次结构,模糊组合对象和简单对象处理问题
代码实现
  1. /**     
  2.  * 组合模式     
  3.  *     
  4.  */
  5.              
  6. //继承模式     
  7.              
  8. class UserBaseInfo {     
  9.     private $name;     
  10.              
  11.     function __construct($name) {     
  12.         $this->name = $name;     
  13.     }     
  14.     public function getName() {     
  15.         return $this->name;     
  16.     }     
  17. }     
  18. class User extends UserBaseInfo {     
  19.     private $login = false;     
  20.                  
  21.     public function setLogin($islogin) {     
  22.         $this->login = $islogin;     
  23.     }     
  24.     public function isLogin() {     
  25.         return $this->login;     
  26.     }     
  27. }     
  28.              
  29. $user = new User('张三');     
  30. $user->setLogin(true);     
  31. if ($user->isLogin()) {     
  32.     echo $user->getName()."已经登录了n";     
  33. } else {     
  34.     echo $user->getName()."还没有登录n";     
  35. }     
  36.              
  37.              
  38. //组合模式     
  39.              
  40. class LoginInfo {     
  41.     protected $user;     
  42.     protected $login = false;     
  43.                  
  44.     public function setLogin($user, $isLogin) {     
  45.         $this->user = $user;     
  46.         $this->login = $isLogin;     
  47.     }     
  48.     public function isLogin() {     
  49.         return $this->login;     
  50.     }     
  51. }     
  52.              
  53. $user = new User('张三');     
  54. $login = new LoginInfo();     
  55. $login->setLogin($user, true);     
  56. if ($login->isLogin()) {     
  57.     echo $user->getName()."已经登录了n";     
  58. } else {     
  59.     echo $user->getName()."还没有登录n";     
  60. }     
  61.              
  62. //部分可以更换,用继承则不行     
  63. class Admin {     
  64.     protected $level;     
  65.     function __construct($level) {     
  66.         $this->level = $level;     
  67.     }     
  68.     function getLevel() {     
  69.         return $this->level;     
  70.     }     
  71. }     
  72. $admin = new Admin(1);     
  73. $login->setLogin($admin, true);     
  74. if ($login->isLogin()) {     
  75.     printf("级别为 %d 的管理员已经登录了n", $admin->getLevel());     
  76. } else {     
  77.     printf("级别为 %d 的管理员还没有登录n", $admin->getLevel());     
  78. }
复制代码
上面的例子分别展示了使用继承和组合来处理新功能,在简单的情况下看似区别不大,但在项目后期越来越复杂的时候组合模式的优势就越来越明显了。
例如上面的登录信息,如果要增加登录次数、最后登录时间、登录ip等信息,登录本身就会变成一个比较复杂的对象。如果以后有新的需求比如好友信息、用户的访问信息等,再要继承的话,用户类就会变得非常庞大,难免各父类之间没有冲突的变量和方法,而外部访问用户类的众多方法也变得很费劲。采用组合模式后,一个类负责一个角色,功能区分非常明显,扩展方便。


5、 外观模式(门面模式)
为了系统中的一组接口提供一个一致的界面
特点:向上抽取,有共性
应用:内部接口众多,由统一的接口来调用

代码实现
  1.           
  2. class Operation {     
  3.     public function testPlus() {     
  4.         printf("plus: %sn", (1 + 2 == 3 ? 'true' : 'false'));     
  5.     }     
  6.     public function testMinus() {     
  7.         printf("minus: %sn", (3 - 2 == 2 ? 'true' : 'false'));     
  8.     }     
  9.     public function testTimes() {     
  10.         printf("times: %sn", (2 * 3 == 6 ? 'true' : 'false'));     
  11.     }     
  12. }     
  13.              
  14. class Tester {     
  15.     protected $_operation;     
  16.     function __construct() {     
  17.         $this->_operation = new Operation();     
  18.     }     
  19.     public function testAll() {     
  20.         $this->_operation->testPlus();     
  21.         $this->_operation->testMinus();     
  22.         $this->_operation->testTimes();     
  23.     }     
  24. }     
  25.              
  26. //测试用例,测试全部接口     
  27. $tester = new Tester();     
  28. $tester->testAll();     
复制代码
门面模式估计大家在实际代码中都已经使用到了,接口较多时把相似功能的接口封装成一个接口供外部调用,这就是门面模式。


6、 享元模式
运用共享技术有效地支持大量细粒度对象,采用一个共享来避免大量有相同内容对象的开销。这种开销中最直观的就是内存的损耗。
特点:高效性,共享性
应用:系统底层的设计。例如字符串的创建。如果两个字符串相同,则不会创建第二个字符串,而是第二个的引用直接指向第一个字符串。$str1=”abc”,$str2=”abc”.则内存存储中只会创建一个字符串“abc”而引用$str1.$str2都会指向它。

7、 代理模式
为其他对象提供一个代理来控制对这个对象的访问,就是给某一对象提供代理对象,并由代理对象控制具体对象的引用。能够协调调用者和被调用者,能够在一定程度上降低系统的耦合性。
特点:低耦合性,独立性好,安全性
应用:客户访问不到或者被访问者希望隐藏自己,所以通过代理来访问自己。

代码实现
  1.             
  2. //内部对象     
  3. class User {     
  4.     public function getName() {     
  5.         return '张三';     
  6.     }     
  7.     public function getType() {     
  8.         return '付费用户';     
  9.     }     
  10. }     
  11.              
  12. //代理接口定义,例如开放平台     
  13. interface UserInterface {     
  14.     function getName();     
  15. }     
  16. //代理对象     
  17. class UserProxy implements UserInterface {     
  18.     protected $_user;     
  19.     function __construct() {     
  20.         $this->_user = new User();     
  21.     }     
  22.     public function getName() {     
  23.         return $this->_user->getName();     
  24.     }     
  25. }     
  26.              
  27. //内部调用     
  28. $user = new User();     
  29. printf("user name:%sn", $user->getName());     
  30. printf("user type:%sn", $user->getType());     
  31. //外部调用     
  32. // $user = new UserProxy();     
  33. // printf("user name:%sn", $user->getName());     
  34. // printf("user type:%sn", $user->getType()); //不能访问,及时知道内部对象有这个方法
复制代码
三、总结
1、代理模式、适配器模式、门面模式、装饰模式的区别
a、 相同之处:都封装一个内部对象,调用内部对象的方法
b、 不同之处:各自有各自的特性和应用场景,不能相互替代。所以用的时候要仔细分析用那种合适。

2、 关于模式的选用问题
模式的选用要根据实际的业务需求,通过对业务逻辑的仔细分析,再根据模式具有的特性和应用场景进行合理的选择和区分。大部分情况下业务的场景决定了哪种模式,而不是选择哪个模式去实现一个业务,少数情况几种模式确实都能解决问题,那主要就是考虑以后的扩展了。

到这里我们已经了解了7种结构型模式,下一篇我们继续给大家介绍设计模式的行为型模式,先预览一下行为型模式的种类吧:
u 模版方法模式
u 命令模式
u 迭代器模式
u 观察者模式
u 终结者模式
u 备忘录模式
u 解释器模式
u 状态模式
u 策略模式
u 职责链模式
u 访问者模式


一、什么是行为型模式?
行为型模式就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。

二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
1、常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、职责链模式、策略模式
2、 已知模式包括:备忘录模式
3、深度模式包括:解释器模式 访问者模式

下面来介绍常见模式
Ø 常见模式
1、模版方法模式(Template):
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。 就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码。
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中。

代码实现

  1. /**
  2.  *
  3.  * 模板方法模式 Template
  4.  *
  5.  */
  6.      
  7. function output($string) {
  8.     echo    $string . "n";
  9. }
  10.      
  11. class Request {
  12.      
  13.     public $token = '';
  14.      
  15.     public function __construct() {
  16.         $this->token    = '0c6b7289f5334ed2b697dd461eaf9812';
  17.     }
  18.      
  19. }
  20.      
  21. class Response {
  22.      
  23.     public function render($content) {
  24.         output(sprintf('response-render: %s', $content));
  25.     }
  26.      
  27.     public function redirect($uri) {
  28.         output(sprintf('response-redirect: %s', $uri));
  29.     }
  30.      
  31.     public function json($data) {
  32.         output(sprintf('response-data: %s', json_encode($data)));
  33.     }
  34.      
  35. }
  36.      
  37.  //父类,抽象类
  38. abstract class Controller{
  39.     //封装了输入输出
  40.     protected $request;
  41.     protected $response;
  42.      
  43.     //返回数据
  44.     protected $data = 'data';
  45.      
  46.     public function __construct($request, $response){
  47.         $this->request = $request;
  48.         $this->response = $response;
  49.     }
  50.      
  51.     //执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
  52.     public final function execute(){
  53.         $this->before();
  54.         if ($this->valid()){
  55.             $this->handleRequest();
  56.         }
  57.         $this->after();
  58.     }
  59.      
  60.     //定义hook method before,做一些具体请求的前置处理
  61.     //非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
  62.     protected function before(){
  63.      
  64.     }
  65.      
  66.     //定义hook method valid,做请求的数据验证
  67.     //非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
  68.     protected function valid(){
  69.         return true;
  70.     }
  71.      
  72.     //定义hook method handleRequest,处理请求
  73.     //定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
  74.     abstract function handleRequest();
  75.      
  76.     //定义hook method after,做一些请求的后置处理
  77.     //非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
  78.     protected function after(){
  79.         $this->response->render($this->data);
  80.     }
  81. }
  82.      
  83. //子类1,实现父类开放的具体算法
  84. class User extends Controller{
  85.     //覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
  86.     //因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
  87.     function before(){
  88.         if (empty($_SESSION['auth'])){
  89.             //没登录就直接跳转了,不再执行后续的操作
  90.             $this->response->redirect("user/login.php");
  91.         }
  92.     }
  93.      
  94.     //覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
  95.     function valid(){
  96.         if (isset($this->request->token)){
  97.             return true;
  98.         }
  99.         return false;
  100.     }
  101.      
  102.     //覆盖handleRequest方法,必选,以为父类中声明了abstract了
  103.     function handleRequest(){
  104.         //做具体处理,一般根据参数执行不同的业务逻辑
  105.     }
  106.      
  107.     //这个类我们选择不覆盖after方法,使用默认处理方式
  108. }
  109.      
  110. //子类2,实现父类开放的具体算法
  111. class Post extends Controller{
  112.     //这个类我们选择不覆盖before方法,使用默认处理方式
  113.      
  114.     //这个类我们选择不覆盖valid方法,使用默认处理方式
  115.      
  116.     //覆盖handleRequest方法,必选,以为父类中声明了abstract了
  117.     function handleRequest(){
  118.         //做具体处理,一般根据参数执行不同的业务逻辑
  119.         $this->data = array('title' => 'ucai');
  120.     }
  121.      
  122.     //覆盖after方法,使用json格式输出数据
  123.     function after(){
  124.         $this->response->json($this->data);
  125.     }
  126. }
  127.      
  128.      
  129.      
  130. class Client {  
  131.            
  132.     public static function test(){  
  133.      
  134.         $request        = new Request();
  135.         $response       = new Response();
  136.      
  137.         //最终调用
  138.         $user = new User($request, $response);
  139.         $user->execute();
  140.      
  141.      
  142.         //最终调用
  143.         $post = new Post($request, $response);
  144.         $post->execute();
  145.      
  146.     }  
  147.            
  148. }  
  149.        
  150. Client::test();
复制代码
2、命令模式(Command) :
行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到 这个命令都会做出标准的敬礼动作。
好处:便于添加和修改行为,便于聚合多个命令。
弊端:造成过多具体的命令类。
应用场景:对要操作的对象,进行的相同操作。

代码实现
  1. /**
  2.  *
  3.  * 命令模式 Command
  4.  *
  5.  */
  6.       
  7. function output($string) {
  8.     echo    $string . "n";
  9. }
  10.       
  11. class Document {
  12.       
  13.     private $name = '';
  14.       
  15.     public function __construct($name) {
  16.         $this->name = $name;
  17.     }
  18.       
  19.     public function showText() {
  20.         output(sprintf("showText: %s", $this->name));
  21.     }
  22.       
  23.     public function undo() {
  24.         output(sprintf("undo-showText: %s", $this->name));
  25.     }
  26.       
  27. }
  28.       
  29. class Graphics {
  30.       
  31.     private $name = '';
  32.       
  33.     public function __construct($name) {
  34.         $this->name = $name;
  35.     }
  36.       
  37.     public function drawCircle() {
  38.         output(sprintf("drawCircle: %s", $this->name));
  39.     }
  40.       
  41.     public function undo() {
  42.         output(sprintf("undo-drawCircle: %s", $this->name));
  43.     }
  44.       
  45. }
  46.       
  47. class Client {
  48.           
  49.     public static function test() {
  50.       
  51.         $document       = new Document('A');
  52.         $graphics       = new Graphics('B');
  53.       
  54.         $document->showText();
  55.         $graphics->drawCircle();
  56.       
  57.         $document->undo();
  58.       
  59.     }
  60.       
  61. }
  62.       
  63. Client::test();
  64.       
  65.       
  66. php
  67.       
  68. /*
  69. *
  70.  * 命令模式 Command
  71.  *
  72.  */
  73.       
  74. function output($string) {
  75.     echo    $string . "n";
  76. }
  77.       
  78. interface Command {
  79.     public function execute();
  80.     public function undo();
  81. }
  82.       
  83. class Document implements Command {
  84.       
  85.     private $name = '';
  86.       
  87.     public function __construct($name) {
  88.         $this->name = $name;
  89.     }
  90.       
  91.     public function execute() {
  92.         output(sprintf("showText: %s", $this->name));
  93.     }
  94.       
  95.     public function undo() {
  96.         output(sprintf("undo-showText: %s", $this->name));
  97.     }
  98.       
  99. }
  100.       
  101. class Graphics implements Command {
  102.       
  103.     private $name = '';
  104.       
  105.     public function __construct($name) {
  106.         $this->name = $name;
  107.     }
  108.       
  109.     public function execute() {
  110.         output(sprintf("drawCircle: %s", $this->name));
  111.     }
  112.       
  113.     public function undo() {
  114.         output(sprintf("undo-drawCircle: %s", $this->name));
  115.     }
  116.       
  117. }
  118.       
  119. class Client {
  120.           
  121.     public static function test() {
  122.       
  123.         $array          = array();
  124.       
  125.         array_push($array, new Document('A'));
  126.         array_push($array, new Document('B'));
  127.         array_push($array, new Graphics('C'));
  128.         array_push($array, new Graphics('D'));
  129.               
  130.         foreach ($array as $command) {
  131.             $command->execute();
  132.         }
  133.       
  134.         $top            = array_pop($array);
  135.         $top->undo();
  136.       
  137.     }
  138.       
  139. }
  140.       
  141. Client::test();
  142.       
  143.       
  144. php
  145.       
  146. /**
  147.  *
  148.  * 命令模式 Command
  149.  *
  150.  */
  151.       
  152. function output($string) {
  153.     echo    $string . "n";
  154. }
  155.       
  156. interface Command {
  157.     public function execute();
  158.     public function undo();
  159. }
  160.       
  161. class Document {
  162.       
  163.     private $name = '';
  164.       
  165.     public function __construct($name) {
  166.         $this->name = $name;
  167.     }
  168.       
  169.     public function showText() {
  170.         output(sprintf("showText: %s", $this->name));
  171.     }
  172.       
  173.     public function undo() {
  174.         output(sprintf("undo-showText: %s", $this->name));
  175.     }
  176.       
  177. }
  178.       
  179. class Graphics {
  180.       
  181.     private $name = '';
  182.       
  183.     public function __construct($name) {
  184.         $this->name = $name;
  185.     }
  186.       
  187.     public function drawCircle() {
  188.         output(sprintf("drawCircle: %s", $this->name));
  189.     }
  190.       
  191.     public function undo() {
  192.         output(sprintf("undo-drawCircle: %s", $this->name));
  193.     }
  194.       
  195. }
  196.       
  197. class DocumentCommand implements Command {
  198.       
  199.     private $obj = '';
  200.       
  201.     public function __construct(Document $document) {
  202.         $this->obj = $document;
  203.     }
  204.       
  205.     public function execute() {
  206.         $this->obj->showText();
  207.     }
  208.       
  209.     public function undo() {
  210.         $this->obj->undo();
  211.     }
  212.       
  213. }
  214.       
  215. class GraphicsCommand implements Command {
  216.       
  217.     private $obj = '';
  218.       
  219.     public function __construct(Graphics $graphics) {
  220.         $this->obj = $graphics;
  221.     }
  222.       
  223.     public function execute() {
  224.         $this->obj->drawCircle();
  225.     }
  226.       
  227.     public function undo() {
  228.         $this->obj->undo();
  229.     }
  230.       
  231. }
  232.       
  233.       
  234. class Client {
  235.           
  236.     public static function test() {
  237.       
  238.         $array          = array();
  239.       
  240.         array_push($array, new DocumentCommand(new Document('A')));
  241.         array_push($array, new DocumentCommand(new Document('B')));
  242.         array_push($array, new GraphicsCommand(new Graphics('C')));
  243.         array_push($array, new GraphicsCommand(new Graphics('D')));
  244.               
  245.         foreach ($array as $command) {
  246.             $command->execute();
  247.         }
  248.       
  249.         $top            = array_pop($array);
  250.         $top->undo();
  251.       
  252.     }
  253.       
  254. }
  255.       
  256. Client::test();
复制代码
3、迭代器模式(Iterator):
访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一 样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。
好处:以不同方式遍历一个集合。
弊端:每次遍历都是整个集合,不能单独取出元素。
应用场景:需要操作集合里的全部元素。

代码实现
  1. /**
  2.  *
  3.  * 迭代器模式 Iterator
  4.  *
  5.  */
  6.       
  7. function output($string) {
  8.     echo    $string . "n";
  9. }
  10.       
  11. class RecordIterator implements Iterator{
  12.       
  13.     private $position = 0;
  14.       
  15.     //注意:被迭代对象属性是私有的
  16.     private $records = array();  
  17.       
  18.     public function __construct(Array $records) {
  19.         $this->position = 0;
  20.         $this->records = $records;
  21.     }
  22.       
  23.     function rewind() {
  24.         $this->position = 0;
  25.     }
  26.       
  27.     function current() {
  28.         return $this->records[$this->position];
  29.     }
  30.       
  31.     function key() {
  32.         return $this->position;
  33.     }
  34.       
  35.     function next() {
  36.         ++$this->position;
  37.     }
  38.       
  39.     function valid() {
  40.         return isset($this->records[$this->position]);
  41.     }
  42. }
  43.       
  44. class PostListPager {
  45.       
  46.     protected $record   = array();
  47.     protected $total    = 0;
  48.     protected $page     = 0;
  49.     protected $size     = 0;
  50.       
  51.     public function __construct($category, $page, $size) {
  52.       
  53.         $this->page     = $page;
  54.         $this->size     = $size;
  55.       
  56.         // query db
  57.       
  58.         $total          = 28;
  59.         $this->total    = $total;
  60.       
  61.         $record     = array(
  62.                         0 => array('id' => '1'),
  63.                         1 => array('id' => '2'),
  64.                         2 => array('id' => '3'),
  65.                         3 => array('id' => '4'),
  66.                     );
  67.       
  68.         //
  69.         $this->record   = $record;
  70.       
  71.     }
  72.       
  73.     public function getIterator() {
  74.         return  new RecordIterator($this->record);
  75.     }
  76.       
  77.     public function getMaxPage() {
  78.         $max    = intval($this->total / $this->size);
  79.         return  $max;
  80.     }
  81.       
  82.     public function getPrevPage() {
  83.         return  max($this->page - 1, 1);
  84.     }
  85.       
  86.     public function getNextPage() {
  87.         return  min($this->page + 1, $this->getMaxPage());
  88.     }
  89.       
  90. }
  91.       
  92. class Client {  
  93.             
  94.     public static function test(){  
  95.       
  96.         $pager      = new PostListPager(1, 2, 4);
  97.       
  98.         foreach ($pager->getIterator() as $key => $val) {
  99.             output(sprintf('Key[%d],Val[%s]', $key, json_encode($val)));
  100.         }
  101.       
  102.         output(sprintf('MaxPage[%d]', $pager->getMaxPage()));
  103.         output(sprintf('Prev[%d]', $pager->getPrevPage()));
  104.         output(sprintf('Next[%d]', $pager->getNextPage()));
  105.       
  106.         $iterator       = $pager->getIterator();
  107.         while($iterator->valid()){
  108.             print_r($iterator->current());
  109.             $iterator->next();
  110.         }
  111.         $iterator->rewind();
  112.       
  113.     }  
  114.             
  115. }  
  116.         
  117. Client::test();
复制代码
4、观察者模式(Observer):
又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察 者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要 是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一 样的。
好处:广播式通信,范围大一呼百应,便于操作一个组团,“公有制”。
弊端:不能单独操作组团里的个体,不能实行按需分配。
应用场景:操作多个对象,并操作相同。

代码实现
  1. /**
  2.  *
  3.  * 观察者模式 Observer
  4.  *
  5.  */
  6.       
  7. function output($string) {
  8.     echo    $string . "n";
  9. }
  10.       
  11.       
  12. //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
  13. //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
  14. class Order{
  15.     //订单号
  16.     private $id = '';
  17.       
  18.     //用户ID
  19.     private $userId = '';
  20.       
  21.     //用户名
  22.     private $userName = '';
  23.       
  24.     //价格
  25.     private $price = '';
  26.       
  27.     //下单时间
  28.     private $orderTime = '';
  29.       
  30.     //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
  31.     public function __set($name, $value){
  32.         if (isset($this->$name)){
  33.             $this->$name = $value;
  34.         }
  35.     }
  36.       
  37.     //获取订单属性
  38.     public function __get($name){
  39.         if (isset($this->$name)){
  40.             return $this->$name;
  41.         }
  42.         return "";
  43.     }
  44. }
  45.       
  46. //假设的DB类,便于测试,实际会存入真实数据库
  47. class FakeDB{
  48.     public function save($data){
  49.         return true;
  50.     }
  51. }
  52.       
  53.       
  54. class Client {
  55.           
  56.     public static function test() {
  57.       
  58.         //初始化一个订单数据
  59.         $order = new Order();
  60.         $order->id = 1001;
  61.         $order->userId = 9527;
  62.         $order->userName = "God";
  63.         $order->price = 20.0;
  64.         $order->orderTime = time();
  65.       
  66.         //向数据库保存订单
  67.         $db = new FakeDB();
  68.         $result = $db->save($order);
  69.         if ($result){
  70.       
  71.             //实际应用可能会写到日志文件中,这里直接输出
  72.             output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
  73.       
  74.             //实际应用会调用邮件发送服务如sendmail,这里直接输出
  75.             output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
  76.       
  77.             //实际应用会调用邮件发送服务如sendmail,这里直接输出
  78.             output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
  79.       
  80.         }
  81.       
  82.     }
  83.       
  84. }
  85.       
  86. Client::test();
  87.       
  88.       
  89. php
  90.       
  91. /**
  92.  *
  93.  * 观察者模式 Observer
  94.  *
  95.  */
  96.       
  97. function output($string) {
  98.     echo    $string . "n";
  99. }
  100.       
  101.       
  102. //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
  103. //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
  104. class Order{
  105.     //订单号
  106.     private $id = '';
  107.       
  108.     //用户ID
  109.     private $userId = '';
  110.       
  111.     //用户名
  112.     private $userName = '';
  113.       
  114.     //价格
  115.     private $price = '';
  116.       
  117.     //下单时间
  118.     private $orderTime = '';
  119.       
  120.     //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
  121.     public function __set($name, $value){
  122.         if (isset($this->$name)){
  123.             $this->$name = $value;
  124.         }
  125.     }
  126.       
  127.     //获取订单属性
  128.     public function __get($name){
  129.         if (isset($this->$name)){
  130.             return $this->$name;
  131.         }
  132.         return "";
  133.     }
  134. }
  135.       
  136. //被观察者, 负责维护观察者并在变化发生是通知观察者
  137. class OrderSubject implements SplSubject {
  138.     private $observers;
  139.     private $order;
  140.       
  141.     public function __construct(Order $order) {
  142.         $this->observers = new SplObjectStorage();
  143.         $this->order = $order;
  144.     }
  145.       
  146.     //增加一个观察者
  147.     public function attach(SplObserver $observer) {
  148.         $this->observers->attach($observer);
  149.     }
  150.       
  151.     //移除一个观察者
  152.     public function detach(SplObserver $observer) {
  153.         $this->observers->detach($observer);
  154.     }
  155.       
  156.     //通知所有观察者
  157.     public function notify() {
  158.         foreach ($this->observers as $observer) {
  159.             $observer->update($this);
  160.         }
  161.     }
  162.       
  163.     //返回主体对象的具体实现,供观察者调用
  164.     public function getOrder() {
  165.         return $this->order;
  166.     }
  167. }
  168.       
  169. //记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
  170. class ActionLogObserver implements SplObserver{
  171.     public function update(SplSubject $subject) {
  172.          $order = $subject->getOrder();
  173.          //实际应用可能会写到日志文件中,这里直接输出
  174.          output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
  175.     }
  176. }
  177.       
  178. //给用户发送订单确认邮件 (UserMailObserver)
  179. class UserMailObserver implements SplObserver{
  180.     public function update(SplSubject $subject) {
  181.          $order = $subject->getOrder();
  182.          //实际应用会调用邮件发送服务如sendmail,这里直接输出
  183.          output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
  184.     }
  185. }
  186.       
  187. //给管理人员发订单处理通知邮件 (AdminMailObserver)
  188. class AdminMailObserver implements SplObserver{
  189.     public function update(SplSubject $subject) {
  190.          $order = $subject->getOrder();
  191.          //实际应用会调用邮件发送服务如sendmail,这里直接输出
  192.          output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
  193.     }
  194. }
  195.       
  196. //假设的DB类,便于测试,实际会存入真实数据库
  197. class FakeDB{
  198.     public function save($data){
  199.         return true;
  200.     }
  201. }
  202.       
  203.       
  204. class Client {
  205.           
  206.     public static function test() {
  207.       
  208.         //初始化一个订单数据
  209.         $order = new Order();
  210.         $order->id = 1001;
  211.         $order->userId = 9527;
  212.         $order->userName = "God";
  213.         $order->price = 20.0;
  214.         $order->orderTime = time();
  215.       
  216.         //绑定观察者
  217.         $subject = new OrderSubject($order);
  218.         $actionLogObserver = new ActionLogObserver();
  219.         $userMailObserver = new UserMailObserver();
  220.         $adminMailObserver = new AdminMailObserver();
  221.         $subject->attach($actionLogObserver);
  222.         $subject->attach($userMailObserver);
  223.         $subject->attach($adminMailObserver);
  224.         //向数据库保存订单
  225.         $db = new FakeDB();
  226.         $result = $db->save($order);
  227.         if ($result){
  228.             //通知观察者
  229.             $subject->notify();
  230.         }
  231.       
  232.     }
  233.       
  234. }
  235.       
  236. Client::test();
复制代码
5、中介者模式(Mediator):
用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引 用。
类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
好处:简化了对象之间的关系,减少子类的生成。
弊端:中介对象可能变得非常复杂,系统难以维护。
应用场景:不需要显示地建立交互

代码实现
  1. /**
  2.  *
  3.  * 中介者模式 Mediator
  4.  *
  5.  */
  6.       
  7.       
  8. function output($string) {
  9.     echo    $string . "n";
  10. }
  11.       
  12.       
  13.       
  14.       
  15. abstract class Mediator { // 中介者角色
  16.     abstract public function send($message,$colleague); 
  17. } 
  18.       
  19. abstract class Colleague { // 抽象对象
  20.     private $_mediator = null; 
  21.     public function __construct($mediator) { 
  22.         $this->_mediator = $mediator; 
  23.     } 
  24.     public function send($message) { 
  25.         $this->_mediator->send($message,$this); 
  26.     } 
  27.     abstract public function notify($message); 
  28. } 
  29.       
  30. class ConcreteMediator extends Mediator { // 具体中介者角色
  31.     private $_colleague1 = null; 
  32.     private $_colleague2 = null; 
  33.     public function send($message,$colleague) { 
  34.         if($colleague == $this->_colleague1) { 
  35.             $this->_colleague1->notify($message); 
  36.         } else { 
  37.             $this->_colleague2->notify($message); 
  38.         } 
  39.     }
  40.     public function set($colleague1,$colleague2) { 
  41.         $this->_colleague1 = $colleague1; 
  42.         $this->_colleague2 = $colleague2; 
  43.     } 
  44. } 
  45.       
  46. class Colleague1 extends Colleague { // 具体对象角色
  47.     public function notify($message) {
  48.         output(sprintf('Colleague-1: %s', $message));
  49.     } 
  50. } 
  51.       
  52. class Colleague2 extends Colleague { // 具体对象角色
  53.     public function notify($message) { 
  54.         output(sprintf('Colleague-2: %s', $message));
  55.     } 
  56. } 
  57.       
  58.       
  59.       
  60. class Client {  
  61.             
  62.     public static function test(){  
  63.       
  64.         // client
  65.         $objMediator = new ConcreteMediator(); 
  66.         $objC1 = new Colleague1($objMediator); 
  67.         $objC2 = new Colleague2($objMediator); 
  68.         $objMediator->set($objC1,$objC2); 
  69.         $objC1->send("to c2 from c1"); 
  70.         $objC2->send("to c1 from c2"); 
  71.       
  72.     }  
  73.             
  74. }  
  75.         
  76. Client::test();
复制代码
6、状态模式(State) :
对象在不同状态下表现出不同的行为。就像女朋友一样,高兴了牵你的 手,不高兴了遛狗。在两种状态下变现出不同的行为。
好处:避免if语句实用,方便增加新状态,封装了状态转换规则。
弊端:增加系统类和对象的数量。
应用场景:用于对象的不同功能的转换。
代码实现
  1. /**
  2.  * 状态模式 State
  3.  *
  4.  */
  5.       
  6. function output($string) {
  7.     echo    $string . "n";
  8. }
  9.       
  10. abstract class ILift {  
  11.       
  12.     //电梯的四个状态  
  13.     const OPENING_STATE = 1;  //门敞状态  
  14.     const CLOSING_STATE = 2;  //门闭状态  
  15.     const RUNNING_STATE = 3;  //运行状态  
  16.     const STOPPING_STATE = 4; //停止状态;  
  17.             
  18.     //设置电梯的状态  
  19.     public abstract function setState($state);  
  20.         
  21.     //首先电梯门开启动作  
  22.     public abstract function open();  
  23.         
  24.     //电梯门有开启,那当然也就有关闭了  
  25.     public abstract function close();  
  26.         
  27.     //电梯要能上能下,跑起来  
  28.     public abstract function run();  
  29.         
  30.     //电梯还要能停下来
  31.     public abstract function stop();  
  32.       
  33. }  
  34.         
  35. /** 
  36.  * 电梯的实现类  
  37.  */ 
  38. class Lift extends ILift {  
  39.       
  40.     private $state;  
  41.         
  42.     public function setState($state) {  
  43.         $this->state = $state;  
  44.     }  
  45.       
  46.     //电梯门关闭  
  47.     public function close() {  
  48.       
  49.         //电梯在什么状态下才能关闭  
  50.         switch ($this->state) {  
  51.             case ILift::OPENING_STATE:  //如果是则可以关门,同时修改电梯状态  
  52.                 $this->setState(ILift::CLOSING_STATE);  
  53.             break;  
  54.             case ILift::CLOSING_STATE:  //如果电梯就是关门状态,则什么都不做  
  55.                 //do nothing;  
  56.                 return ;  
  57.             break;  
  58.             case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做  
  59.                 //do nothing;  
  60.                 return ;  
  61.             break;  
  62.             case ILift::STOPPING_STATE:  //如果是停止状态,本也是关闭的,什么也不做  
  63.                 //do nothing;  
  64.                 return ;  
  65.             break;  
  66.         }  
  67.       
  68.         output('Lift colse');  
  69.       
  70.     }  
  71.         
  72.     //电梯门开启  
  73.     public function open() {  
  74.         //电梯在什么状态才能开启  
  75.         switch($this->state){  
  76.             case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做  
  77.                 //do nothing;  
  78.                 return ;  
  79.             break;  
  80.             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启  
  81.                 $this->setState(ILift::OPENING_STATE);  
  82.             break;  
  83.             case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做  
  84.             //do nothing;  
  85.                 return ;  
  86.             break;  
  87.             case ILift::STOPPING_STATE: //停止状态,淡然要开门了  
  88.                 $this->setState(ILift::OPENING_STATE);  
  89.             break;  
  90.         }  
  91.         output('Lift open');  
  92.     }  
  93.     ///电梯开始跑起来  
  94.     public function run() {  
  95.         switch($this->state){  
  96.             case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做  
  97.                 //do nothing;  
  98.                 return ;  
  99.             break;  
  100.             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行  
  101.                 $this->setState(ILift::RUNNING_STATE);  
  102.             break;  
  103.             case ILift::RUNNING_STATE: //正在运行状态,则什么都不做  
  104.                 //do nothing;  
  105.                 return ;  
  106.             break;  
  107.             case ILift::STOPPING_STATE: //停止状态,可以运行  
  108.                 $this->setState(ILift::RUNNING_STATE);  
  109.         }  
  110.         output('Lift run');  
  111.     }  
  112.         
  113.     //电梯停止  
  114.     public function stop() {  
  115.         switch($this->state){  
  116.             case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做  
  117.                 //do nothing;  
  118.                 return ;  
  119.             break;  
  120.             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了  
  121.                 $this->setState(ILift::CLOSING_STATE);  
  122.             break;  
  123.             case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了  
  124.                 $this->setState(ILift::CLOSING_STATE);  
  125.             break;  
  126.             case ILift::STOPPING_STATE: //停止状态,什么都不做  
  127.                 //do nothing;  
  128.                 return ;  
  129.             break;  
  130.         }  
  131.         output('Lift stop');  
  132.     }  
  133.             
  134. }  
  135.       
  136.       
  137.       
  138. class Client {
  139.           
  140.     public static function test() {
  141.       
  142.         $lift = new Lift();   
  143.                    
  144.         //电梯的初始条件应该是停止状态   
  145.         $lift->setState(ILift::STOPPING_STATE);   
  146.         //首先是电梯门开启,人进去   
  147.         $lift->open();   
  148.                    
  149.         //然后电梯门关闭   
  150.         $lift->close();   
  151.                    
  152.         //再然后,电梯跑起来,向上或者向下   
  153.         $lift->run();      
  154.       
  155.          //最后到达目的地,电梯挺下来   
  156.         $lift->stop();  
  157.       
  158.     }
  159.       
  160. }
  161.       
  162. Client::test();
  163.       
  164.       
  165. php
  166.       
  167. /**
  168.  *
  169.  * 状态模式 State
  170.  *
  171.  */
  172.       
  173. function output($string) {
  174.     echo    $string . "n";
  175. }
  176.       
  177. /** 
  178.  *  
  179.  * 定义一个电梯的接口  
  180.  */ 
  181. abstract class LiftState{  
  182.         
  183.     //定义一个环境角色,也就是封装状态的变换引起的功能变化  
  184.     protected  $_context;  
  185.         
  186.     public function setContext(Context $context){  
  187.         $this->_context = $context;  
  188.     }  
  189.         
  190.     //首先电梯门开启动作  
  191.     public abstract function open();  
  192.         
  193.     //电梯门有开启,那当然也就有关闭了  
  194.     public abstract function close();  
  195.         
  196.     //电梯要能上能下,跑起来  
  197.     public abstract function run();  
  198.         
  199.     //电梯还要能停下来,停不下来那就扯淡了  
  200.     public abstract function stop();  
  201.         
  202. }  
  203.         
  204.         
  205. /** 
  206.  * 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。 
  207.  */ 
  208. class Context {  
  209.     //定义出所有的电梯状态  
  210.     static  $openningState = null;  
  211.     static  $closeingState = null;  
  212.     static  $runningState  = null;  
  213.     static  $stoppingState = null;  
  214.         
  215.     public function __construct() {  
  216.         self::$openningState = new OpenningState();  
  217.         self::$closeingState = new ClosingState();  
  218.         self::$runningState =  new RunningState();  
  219.         self::$stoppingState = new StoppingState();  
  220.         
  221.     }  
  222.         
  223.     //定一个当前电梯状态  
  224.     private  $_liftState;  
  225.         
  226.     public function getLiftState() {  
  227.         return $this->_liftState;  
  228.     }  
  229.         
  230.     public function setLiftState($liftState) {  
  231.         $this->_liftState = $liftState;  
  232.         //把当前的环境通知到各个实现类中  
  233.         $this->_liftState->setContext($this);  
  234.     }  
  235.         
  236.         
  237.     public function open(){  
  238.         $this->_liftState->open();  
  239.     }  
  240.         
  241.     public function close(){  
  242.         $this->_liftState->close();  
  243.     }  
  244.         
  245.     public function run(){  
  246.         $this->_liftState->run();  
  247.     }  
  248.         
  249.     public function stop(){  
  250.         $this->_liftState->stop();  
  251.     }  
  252. }  
  253.         
  254. /** 
  255.  * 在电梯门开启的状态下能做什么事情  
  256.  */ 
  257. class OpenningState extends LiftState {  
  258.         
  259.     /** 
  260.      * 开启当然可以关闭了,我就想测试一下电梯门开关功能 
  261.      * 
  262.      */
  263.     public function close() {  
  264.         //状态修改  
  265.         $this->_context->setLiftState(Context::$closeingState);  
  266.         //动作委托为CloseState来执行  
  267.         $this->_context->getLiftState()->close();  
  268.     }  
  269.         
  270.     //打开电梯门  
  271.     public function open() {  
  272.         output('lift open...');
  273.     }  
  274.     //门开着电梯就想跑,这电梯,吓死你!  
  275.     public function run() {  
  276.         //do nothing;  
  277.     }  
  278.         
  279.     //开门还不停止?  
  280.     public function stop() {  
  281.         //do nothing;  
  282.     }  
  283.         
  284. }  
  285.         
  286. /** 
  287.  * 电梯门关闭以后,电梯可以做哪些事情  
  288.  */ 
  289. class ClosingState extends LiftState {  
  290.         
  291.     //电梯门关闭,这是关闭状态要实现的动作  
  292.     public function close() {  
  293.         output('lift close...');
  294.         
  295.     }  
  296.     //电梯门关了再打开,逗你玩呢,那这个允许呀  
  297.     public function open() {  
  298.         $this->_context->setLiftState(Context::$openningState);  //置为门敞状态  
  299.         $this->_context->getLiftState()->open();  
  300.     }  
  301.         
  302.     //电梯门关了就跑,这是再正常不过了  
  303.     public function run() {  
  304.         $this->_context->setLiftState(Context::$runningState); //设置为运行状态;  
  305.         $this->_context->getLiftState()->run();  
  306.     }  
  307.         
  308.     //电梯门关着,我就不按楼层  
  309.             
  310.     public function stop() {  
  311.         $this->_context->setLiftState(Context::$stoppingState);  //设置为停止状态;  
  312.         $this->_context->getLiftState()->stop();  
  313.     }  
  314.         
  315. }  
  316.         
  317. /** 
  318.  * 电梯在运行状态下能做哪些动作  
  319.  */ 
  320. class RunningState extends LiftState {  
  321.         
  322.     //电梯门关闭?这是肯定了  
  323.     public function close() {  
  324.         //do nothing  
  325.     }  
  326.         
  327.     //运行的时候开电梯门?你疯了!电梯不会给你开的  
  328.     public function open() {  
  329.         //do nothing  
  330.     }  
  331.         
  332.     //这是在运行状态下要实现的方法  
  333.     public function run() {  
  334.         output('lift run...');
  335.     }  
  336.         
  337.     //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了  
  338.     public function stop() {  
  339.         $this->_context->setLiftState(Context::$stoppingState); //环境设置为停止状态;  
  340.         $this->_context->getLiftState()->stop();  
  341.     }  
  342.         
  343. }  
  344.         
  345.         
  346.         
  347. /** 
  348.  * 在停止状态下能做什么事情  
  349.  */ 
  350. class StoppingState extends LiftState {  
  351.         
  352.     //停止状态关门?电梯门本来就是关着的!  
  353.     public function close() {  
  354.         //do nothing;  
  355.     }  
  356.         
  357.     //停止状态,开门,那是要的!  
  358.     public function open() {  
  359.         $this->_context->setLiftState(Context::$openningState);  
  360.         $this->_context->getLiftState()->open();  
  361.     }  
  362.     //停止状态再跑起来,正常的很  
  363.     public function run() {  
  364.         $this->_context->setLiftState(Context::$runningState);  
  365.         $this->_context->getLiftState()->run();  
  366.     }  
  367.     //停止状态是怎么发生的呢?当然是停止方法执行了  
  368.     public function stop() {  
  369.         output('lift stop...');
  370.     }  
  371.         
  372. }  
  373.         
  374. /** 
  375.  * 模拟电梯的动作  
  376.  */ 
  377. class Client {  
  378.         
  379.     public static function test() {  
  380.         $context = new Context();  
  381.         $context->setLiftState(new ClosingState());  
  382.         
  383.         $context->open();  
  384.         $context->close();  
  385.         $context->run();  
  386.         $context->stop();  
  387.     }  
  388. }  
  389.       
  390. Client::test();
复制代码
7、职责链模式 (Chainof Responsibility):
多个对象有机会处理请求,为请求发送者和接收者解耦。就像银行里 的取款机,不管那一台都可以取到钱。
好处:简单化对象隐藏链结构,便于添加新职责节点。
弊端:请求可能没有接受者,或者被多个接收者调用,性能降低。
应用场景:处理多种请求。

代码实现

你可能感兴趣的:(php)