设计模式详解及PHP实现

设计模式(Design Patterns)

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 –百度百科

In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. –Wikipedia

在软件开发过程中,一个功能的实现方式多种多样,不同方法的可扩展性、可维护性以及复用性都是不一样的。随着一个人对自己项目代码的要求增加,他会逐渐思考和实践出自己的一套方法或者思想,这种方法或思想决定了他设计出的架构或者编写出的代码的质量优劣。设计模式就属于这样一种经验的积累,是由大量优秀的工程师或者架构师总结和提炼的精华,学习好设计模式等于让我们站在了巨人的肩膀上,从一个高的起点出发,可以避免走很多弯路。

设计模式的使用一定是根据场景来选择的,而且设计模式的实现方式也不是固定的,我们一定要在理解现有设计模式的基础上,根据自己实际的情况不断实践不断理解。就像所谓的《泡妞大全》,读千万遍都不如实践一次来的实际。

如果你对设计模式完全没有感觉,那么去好好写一个类库,或者一个简单的MVC框架,这个过程会让你感觉到自己缺失的部分。

设计模式的优点:

        1).复用解决方案: 设计模式本身就是对某一类问题的通用解决方案,是更高级别的复用,已经超出了代码复用.
        2).确定通用术语:开发中的交流和协作都需要共同的词汇其础和对问题的共识. 当你有想表达却又表达不清楚的设计思路,即使表达出来也会被同事误解的时候,设计模式就显出沟通的优势了。

        3).代码更易于修改与维护。因为设计模式都是久经考验的解决方案,它们的结构都是经过长期的发展形成的,善于应对变化,设计模式本身也是对变化点的封装。

        4).模式有助于提高思考层次。学习模式后,就算不用模式中的方法,也会更好的采取更好的策略去解决问题。

六大原则
1、单一原则:一个类只负责功能领域中的相应职责(如 一个老师只管教育学生。)

单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。

2、开闭原则:一个软件实体应当对扩展开放,对修改关闭。(如 坐飞机,你只能乘坐,不能对他的飞行轨迹进行指控。但是如果你有需要,可以根据不同的线路来乘坐。)

3、里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。(如: 我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能确定我喜欢其他动物。)

4、依赖倒置原则:抽象不应改依赖于细节,细节依赖与抽象。 (要针对接口编程,而不是针对实现编程。)

5、接口隔离原则:使用多个专门的接口,而不是统一的接口,把功能细分化。使客户端在调用的时候不依赖他不需要的接口。

6、迪米特法则:一个软件实体,尽量少的与其他实体相互的发生作用。

分类

在《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software) 这本书中,作者把设计模式分了三大类:

创建型模式(Creational patterns)

创建型模式是为了解决创建对象时候遇到的问题。因为基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式有两个主导思想:一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合方式。

最常见的五种创建型模式如下:

  • 工厂方法模式(Factory method pattern)
  • 抽象工厂模式(Abstract factory pattern)
  • 单例模式(Singleton pattern)
  • 建造者模式(Builder pattern)
  • 原型模式(Prototype pattern)

结构型模式(Structural pattern)

结构型模式是通过定义一个简单的方法来实现和了解实体间关系,从而简化设计。

  • 适配器模式(Adapter pattern)
  • 桥接模式(Bridge pattern)
  • 合成模式(Composite pattern)
  • 装饰器模式(Decorator pattern)
  • 门面模式(Facade pattern)
  • 代理模式(Proxy pattern)
  • 享元模式(Flyweight Pattern)

行为型模式(Behavioral pattern)

行为型模式用来识别对象之间的常用交流模式并加以实现,使得交流变得更加灵活。

  • 策略模式(Strategy pattern)
  • 模板方法模式(Template method pattern)
  • 观察者模式(Observer pattern)
  • 迭代器模式(Iterator pattern)
  • 责任链模式(Chain of responsibility pattern)
  • 命令模式(Command pattern)
  • 备忘录模式(Memento pattern)
  • 状态模式(State pattern)
  • 访问者模式(Visitor pattern)
  • 中介者模式(Mediator pattern)
  • 解释器模式(Interpreter pattern)

关系

这里有一张各个模式关系图,可以在了解各个模式以后梳理一下

参考

  1. Wikipedia: Software design pattern
  2. 百度百科:设计模式

建造者模式(Builder pattern)

建造者模式是一种创建型模式,它可以让一个产品的内部表象和和产品的生产过程分离开,从而可以生成具有不同内部表象的产品。

Builder模式中主要角色

  • 抽象建造者(Builder)角色:定义抽象接口,规范产品各个部分的建造,必须包括建造方法和返回方法。
  • 具体建造者(Concrete)角色:实现抽象建造者接口。应用程序最终根据此角色中实现的业务逻辑创造产品。
  • 导演者(Director)角色:调用具体的建造者角色创造产品。
  • 产品(Product)角色:在导演者的指导下所创建的复杂对象。

适用性

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。

类图

实例


  1. php
  2. class Product { // 产品本身
  3.    private $_parts;
  4.    public function __construct() { $this->_parts = array(); }
  5.    public function add($part) { return array_push($this->_parts, $part); }
  6. }
  7. abstract class Builder { // 建造者抽象类
  8.    public abstract function buildPart1();
  9.    public abstract function buildPart2();
  10.    public abstract function getResult();
  11. }
  12. class ConcreteBuilder extends Builder { // 具体建造者
  13.    private $_product;
  14.    public function __construct() { $this->_product = new Product(); }
  15.    public function buildPart1() { $this->_product->add("Part1"); }
  16.    public function buildPart2() { $this->_product->add("Part2"); }
  17.    public function getResult() { return $this->_product; }
  18. }
  19. class Director {
  20.    public function __construct(Builder $builder) {
  21.        $builder->buildPart1();
  22.        $builder->buildPart2();
  23.    }
  24. }
  25. // client
  26. $buidler = new ConcreteBuilder();
  27. $director = new Director($buidler);
  28. $product = $buidler->getResult();
  29. ?>

优缺点

优点

建造者模式可以很好的将一个对象的实现与相关的“业务”逻辑分离开来,从而可以在不改变事件逻辑的前提下,使增加(或改变)实现变得非常容易。

缺点

建造者接口的修改会导致所有执行类的修改。

参考

  1. Wikipedia: Bulider pattern
  2. Wikipedia: 生成器模式
  3. PHP设计模式笔记:使用PHP实现建造者模式

单例模式(Singleton pattern)

抽象工厂模式是一种创建型模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在。

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

单例模式中主要角色

Singleton定义一个getInstance操作,允许客户访问它唯一的实例。

这个例子也简单,就像我有6个老婆(快醒醒!),她们在喊”老公”的时候都是指我。不管什么时候,喊老公擦地,做饭,洗衣服都是指同一个人,PHP不编写多线程,所以不存在抢占问题,如果换别的语言编写,一定得考虑到抢占问题!老公是不可以边擦地边做饭的!

适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
  • 当这个唯一实例应该是通过子类化可扩展的。并且用户应该无需更改代码就能使用一个扩展的实例时。

类图

实例


  1. php
  2. public class Singleton {
  3.    private static $_instance = NULL;
  4.    // 私有构造方法
  5.    private function __construct() {}
  6.    public static function getInstance() {
  7.        if (is_null(self::$_instance)) {
  8.            self::$_instance = new Singleton();
  9.        }
  10.        return self::$_instance;
  11.    }
  12.    // 防止克隆实例
  13.    public function __clone(){
  14.        die('Clone is not allowed.' . E_USER_ERROR);
  15.    }
  16. }
  17. ?>

在此实例中,Singleton禁止了克隆及外部初始化,使得此类只可以通过getInstance()方法来获得实例,而这个实例只会在第一次使用时创建,以后每次都获得同一实例。

优缺点

#### 优点
- 对唯一实例的受控访问
- 缩小命名空间 单例模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染命名空间
- 允许对操作和表示的精华,单例类可以有子类。而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
- 允许可变数目的实例(多例模式)
- 比类操作更灵活

缺点

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

参考

  1. Wikipedia: 单例模式
  2. Wikipedia: Singleton pattern
  3. PHP设计模式笔记:使用PHP实现单例模式

适配器模式(Adapter pattern)

适配器模式是一种结构型模式,它将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类别自己的接口包裹在一个已存在的类中。

适配器模式中主要角色

  • 目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的
  • 源(Adaptee)角色:需要进行适配的接口
  • 适配器(Adapter)角色:对Adaptee的接口与Target接口进行适配;适配器是本模式的核心,适配器把源接口转换成目标接口,此角色为具体类

适用性

1、你想使用一个已经存在的类,而它的接口不符合你的需求
2、你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作
3、你想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口(仅限于对象适配器)

类适配器模式与对象适配器

类适配器:Adapter与Adaptee是继承关系

  • 用一个具体的Adapter类和Target进行匹配。结果是当我们想要一个匹配一个类以及所有它的子类时,类Adapter将不能胜任工作
  • 使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子集
  • 仅仅引入一个对象,并不需要额外的指针以间接取得adaptee

对象适配器:Adapter与Adaptee是委托关系

  • 允许一个Adapter与多个Adaptee同时工作。Adapter也可以一次给所有的Adaptee添加功能
  • 使用重定义Adaptee的行为比较困难

类图

类适配器

对象适配器

实例

类适配器


  1. php
  2. interface Target {
  3.    public function sampleMethod1();
  4.    public function sampleMethod2();
  5. }
  6. class Adaptee { // 源角色
  7.    public function sampleMethod1() {}
  8. }
  9. class Adapter extends Adaptee implements Target { // 适配后角色
  10.    public function sampleMethod2() {}
  11. }
  12. // client
  13. $adapter = new Adapter();
  14. $adapter->sampleMethod1();
  15. $adapter->sampleMethod2();
  16. ?>

对象适配器


  1. php
  2. interface Target {
  3.    public function sampleMethod1();
  4.    public function sampleMethod2();
  5. }
  6. class Adaptee {
  7.    public function sampleMethod1() {}
  8. }
  9. class Adapter implements Target {
  10.    private $_adaptee;
  11.    public function __construct(Adaptee $adaptee) {
  12.        $this->_adaptee = $adaptee;
  13.    }
  14.    public function sampleMethod1() { $this->_adaptee->sampleMethod1(); }
  15.    public function sampleMethod2() {}
  16. }
  17. $adaptee = new Adaptee();
  18. $adapter = new Adapter($adaptee);
  19. $adapter->sampleMethod1();
  20. $adapter->sampleMethod2();
  21. ?>

参考

  1. Wikipedia: Adapter pattern
  2. Wikipedia: 适配器模式
  3. PHP设计模式笔记:使用PHP实现适配器模式

桥接模式(Bridge pattern)

桥接模式是一种结构型模式,它是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。

主要角色

  • 抽象化(Abstraction)角色:定义抽象类的接口并保存一个对实现化对象的引用。
  • 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:定义实现类的接口,不给出具体的实现。此接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以完全不同。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(Concrete Implementor)角色:实现实现化角色接口并定义它的具体实现。

适用性

  • 如果一个系统需要在构件的抽象化和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
  • 设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
  • 一个构件有多于一个的抽象化角色和实现化角色,并且系统需要它们之间进行动态的耦合。
  • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。

类图

实例


  1. php
  2. abstract class Abstraction { // 抽象化角色,抽象化给出的定义,并保存一个对实现化对象的引用。    
  3.    protected $imp; // 对实现化对象的引用
  4.    public function operation() {
  5.        $this->imp->operationImp();
  6.    }
  7. }
  8. class RefinedAbstraction extends Abstraction { // 修正抽象化角色, 扩展抽象化角色,改变和修正父类对抽象化的定义。
  9.     public function __construct(Implementor $imp) {
  10.        $this->imp = $imp;
  11.    }
  12.    public function operation() { $this->imp->operationImp(); }
  13. }
  14. abstract class Implementor { // 实现化角色, 给出实现化角色的接口,但不给出具体的实现。
  15.    abstract public function operationImp();
  16. }
  17. class ConcreteImplementorA extends Implementor { // 具体化角色A
  18.    public function operationImp() {}
  19. }
  20. class ConcreteImplementorB extends Implementor { // 具体化角色B
  21.    public function operationImp() {}
  22. }
  23. // client
  24. $abstraction = new RefinedAbstraction(new ConcreteImplementorA());
  25. $abstraction->operation();
  26. $abstraction = new RefinedAbstraction(new ConcreteImplementorB());
  27. $abstraction->operation();
  28. ?>

优点

  • 分离接口及其实现部分, 将Abstraction与Implementor分享有助于降低对实现部分编译时刻的依赖性, 接口与实现分享有助于分层,从而产生更好的结构化系统
  • 提高可扩充性
  • 实现细节对客户透明。

参考

  1. Wikipedia: Bridge pattern
  2. Wikipedia: 桥接模式
  3. PHP设计模式笔记:使用PHP实现桥接模式

合成模式(Composite pattern)

合成模式是一种结构型模式,它将对象组合成树形结构以表示”部分-整体”的层次结构。Composite使用户对单个对象和组合对象的使用具有一致性。
Composite变化的是一个对象的结构和组成。

主要角色

  • 抽象组件(Component)角色:抽象角色,给参加组合的对象规定一个接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子组件
  • 树叶组件(Leaf)角色:在组合中表示叶节点对象,叶节点没有子节点。在组合中定义图元对象的行为。
  • 树枝组件(Composite)角色:存储子部件。定义有子部件的那些部件的行为。在Component接口中实现与子部件有关的操作。
  • 客户端(Client):通过Component接口操纵组合部件的对象

适用性

  • 你想表示对象的部分-整体层次结构。
  • 你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象。

类图

安全式合成模式

透明式合成模式

实例

安全式合成模式

在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的。因为树叶类型的对象根本就没有管理子类的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。编译通不过,就不会出现运行时期错误。这样的缺点是不够透明,因为树叶类和合成类将具有不同的接口。


  1. php
  2. interface Component {
  3.    public function getComposite(); //返回自己的实例
  4.    public function operation();
  5. }
  6. class Composite implements Component { // 树枝组件角色
  7.    private $_composites;
  8.    public function __construct() { $this->_composites = array(); }
  9.    public function getComposite() { return $this; }
  10.     public function operation() {
  11.         foreach ($this->_composites as $composite) {
  12.            $composite->operation();
  13.        }
  14.     }
  15.    public function add(Component $component) {  //聚集管理方法 添加一个子对象
  16.        $this->_composites[] = $component;
  17.    }
  18.    public function remove(Component $component) { // 聚集管理方法 删除一个子对象
  19.        foreach ($this->_composites as $key => $row) {
  20.            if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
  21.        }
  22.        return FALSE;
  23.    }
  24.    public function getChild() { // 聚集管理方法 返回所有的子对象
  25.       return $this->_composites;
  26.    }
  27. }
  28. class Leaf implements Component {
  29.    private $_name;
  30.    public function __construct($name) { $this->_name = $name; }
  31.    public function operation() {}
  32.    public function getComposite() {return null;}
  33. }
  34. // client
  35. $leaf1 = new Leaf('first');
  36. $leaf2 = new Leaf('second');
  37. $composite = new Composite();
  38. $composite->add($leaf1);
  39. $composite->add($leaf2);
  40. $composite->operation();
  41. $composite->remove($leaf2);
  42. $composite->operation();
  43. ?>

透明式合成模式

在Composite类里面声明所有的用来管理子类对象的方法。这样做的是好处是所有的组件类都有相同的接口。在客户端看来,树叶类和合成类对象的区别起码在接口层次上消失了,客户端可以同等的对待所有的对象。这就是透明形式的合成模式,缺点就是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此调用其添加或删除方法就没有意义了,这在编译期间是不会出错的,而只会在运行时期才会出错。


  1. php
  2. interface Component { // 抽象组件角色
  3.    public function getComposite(); // 返回自己的实例
  4.    public function operation(); // 示例方法
  5.    public function add(Component $component); // 聚集管理方法,添加一个子对象
  6.    public function remove(Component $component); // 聚集管理方法 删除一个子对象
  7.    public function getChild(); // 聚集管理方法 返回所有的子对象
  8. }
  9. class Composite implements Component { // 树枝组件角色
  10.    private $_composites;
  11.    public function __construct() { $this->_composites = array(); }
  12.    public function getComposite() { return $this; }
  13.    public function operation() { // 示例方法,调用各个子对象的operation方法
  14.        foreach ($this->_composites as $composite) {
  15.            $composite->operation();
  16.        }
  17.    }
  18.    public function add(Component $component) { // 聚集管理方法 添加一个子对象
  19.        $this->_composites[] = $component;
  20.    }
  21.    public function remove(Component $component) { // 聚集管理方法 删除一个子对象
  22.        foreach ($this->_composites as $key => $row) {
  23.            if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
  24.        }
  25.        return FALSE;
  26.    }
  27.    public function getChild() { // 聚集管理方法 返回所有的子对象
  28.       return $this->_composites;
  29.    }
  30. }
  31. class Leaf implements Component {
  32.    private $_name;
  33.    public function __construct($name) {$this->_name = $name;}
  34.    public function operation() {}
  35.    public function getComposite() { return null; }
  36.    public function add(Component $component) { return FALSE; }
  37.    public function remove(Component $component) { return FALSE; }
  38.    public function getChild() { return null; }
  39. }
  40. // client
  41. $leaf1 = new Leaf('first');
  42. $leaf2 = new Leaf('second');
  43. $composite = new Composite();
  44. $composite->add($leaf1);
  45. $composite->add($leaf2);
  46. $composite->operation();
  47. $composite->remove($leaf2);
  48. $composite->operation();
  49. ?>

优缺点

#### 优点

  • 简化客户代码
  • 使得更容易增加新类型的组件

缺点

  • 使你的设计变得更加一般化,容易增加组件也会产生一些问题,那就是很难限制组合中的组件

参考

  1. Wikipedia: Composite pattern
  2. PHP设计模式笔记:使用PHP实现合成模式

装饰器模式(Decorator pattern)

装饰器模式是一种结构型模式,它动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活【GOF95】
装饰模式是以对客户透明的方式动态地给一个对象附加上更多的职责。这也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

主要角色

  • 抽象构件(Component)角色:定义一个对象接口,以规范准备接收附加职责的对象,从而可以给这些对象动态地添加职责。
  • 具体构件(Concrete Component)角色:定义一个将要接收附加职责的类。
  • 装饰(Decorator)角色:持有一个指向Component对象的指针,并定义一个与Component接口一致的接口。
  • 具体装饰(Concrete Decorator)角色:负责给构件对象增加附加的职责。

适用性

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 处理那些可以撤消的职责,即需要动态的给一个对象添加功能并且这些功能是可以动态的撤消的。
  • 当不能彩生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

类图

实例


  1. php
  2. interface Component {
  3.    public function operation();
  4. }
  5. abstract class Decorator implements Component{ // 装饰角色
  6.    protected  $_component;
  7.    public function __construct(Component $component) {
  8.        $this->_component = $component;
  9.    }
  10.    public function operation() {
  11.        $this->_component->operation();
  12.    }
  13. }
  14. class ConcreteDecoratorA extends Decorator { // 具体装饰类A
  15.    public function __construct(Component $component) {
  16.        parent::__construct($component);
  17.    }
  18.    public function operation() {
  19.        parent::operation();    //  调用装饰类的操作
  20.        $this->addedOperationA();   //  新增加的操作
  21.    }
  22.    public function addedOperationA() {}
  23. }
  24. class ConcreteDecoratorB extends Decorator { // 具体装饰类B
  25.    public function __construct(Component $component) {
  26.        parent::__construct($component);
  27.    }
  28.    public function operation() {
  29.        parent::operation();
  30.        $this->addedOperationB();
  31.    }
  32.    public function addedOperationB() {}
  33. }
  34. class ConcreteComponent implements Component{
  35.    public function operation() {}
  36. }
  37. // clients
  38. $component = new ConcreteComponent();
  39. $decoratorA = new ConcreteDecoratorA($component);
  40. $decoratorB = new ConcreteDecoratorB($decoratorA);
  41. $decoratorA->operation();
  42. $decoratorB->operation();
  43. ?>

优缺点

优点

  • 比静态继承更灵活;
  • 避免在层次结构高层的类有太多的特征

缺点

  • 使用装饰模式会产生比使用继承关系更多的对象。并且这些对象看上去都很想像,从而使得查错变得困难。

参考

  1. Wikipedia: Decorator pattern
  2. Wikipedia: 修饰模式
  3. PHP设计模式笔记:使用PHP实现装饰模式

门面模式(Facade pattern)

门面模式是一种结构型模式,它为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层次的接口,使得子系统更加容易使用。

主要角色

门面(Facade)角色

  • 此角色将被客户端调用
  • 知道哪些子系统负责处理请求
  • 将用户的请求指派给适当的子系统

子系统(subsystem)角色

  • 实现子系统的功能
  • 处理由Facade对象指派的任务
  • 没有Facade的相关信息,可以被客户端直接调用
  • 可以同时有一个或多个子系统,每个子系统都不是一个单独的类,而一个类的集合。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并知道门面模式的存在,对于子系统而言,门面仅仅是另一个客户端。

适用性

  • 为一些复杂的子系统提供一组接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用门面模式定义系统的每一层的接口

类图

实例


  1. php
  2. class Camera {
  3.    public function turnOn() {}
  4.    public function turnOff() {}
  5.    public function rotate($degrees) {}
  6. }
  7. class Light {
  8.    public function turnOn() {}
  9.    public function turnOff() {}
  10.    public function changeBulb() {}
  11. }
  12. class Sensor {
  13.    public function activate() {}
  14.    public function deactivate() {}
  15.    public function trigger() {}
  16. }
  17. class Alarm {
  18.    public function activate() {}
  19.    public function deactivate() {}
  20.    public function ring() {}
  21.    public function stopRing() {}
  22. }
  23. class SecurityFacade {
  24.    private $_camera1, $_camera2;
  25.    private $_light1, $_light2, $_light3;
  26.    private $_sensor;
  27.    private $_alarm;
  28.    public function __construct() {
  29.        $this->_camera1 = new Camera();
  30.        $this->_camera2 = new Camera();
  31.        $this->_light1 = new Light();
  32.        $this->_light2 = new Light();
  33.        $this->_light3 = new Light();
  34.        $this->_sensor = new Sensor();
  35.        $this->_alarm = new Alarm();
  36.    }
  37.    public function activate() {
  38.        $this->_camera1->turnOn();
  39.        $this->_camera2->turnOn();
  40.        $this->_light1->turnOn();
  41.        $this->_light2->turnOn();
  42.        $this->_light3->turnOn();
  43.        $this->_sensor->activate();
  44.        $this->_alarm->activate();
  45.    }
  46.    public  function deactivate() {
  47.        $this->_camera1->turnOff();
  48.        $this->_camera2->turnOff();
  49.        $this->_light1->turnOff();
  50.        $this->_light2->turnOff();
  51.        $this->_light3->turnOff();
  52.        $this->_sensor->deactivate();
  53.        $this->_alarm->deactivate();
  54.    }
  55. }
  56. //client
  57. $security = new SecurityFacade();
  58. $security->activate();
  59. ?>

优缺点

优点

  • 它对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便
  • 实现了子系统与客户之间的松耦合关系
  • 如果应用需要,它并不限制它们使用子系统类。因此可以在系统易用性与能用性之间加以选择

参考

  1. Wikipedia: Facade pattern
  2. Wikipedia: 外观模式
  3. PHP设计模式笔记:使用PHP实现门面模式

享元模式(Flyweight Pattern)

享元模式是一种结构型模式,它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。

主要角色

  • 抽象享元(Flyweight角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态的操作可以通过调用商业以参数形式传入
  • 具体享元(ConcreteFlyweight角色:实现Flyweight接口,并为内部状态(如果有的话)拉回存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的
  • 不共享的具体享元(UnsharedConcreteFlyweight)角色:并非所有的Flyweight子类都需要被共享。Flyweigth使共享成为可能,但它并不强制共享。
  • 享元工厂(FlyweightFactory)角色:负责创建和管理享元角色。本角色必须保证享元对象可能被系统适当地共享
  • 客户端(Client)角色:本角色需要维护一个对所有享元对象的引用。本角色需要自行存储所有享元对象的外部状态

适用性

  • 一个应用程序使用了大量的对象
  • 完全由于使用大量的对象,造成很大的存储开销
  • 对象的大多数状态都可变为外部状态
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
  • 应用程序不依赖于对象标识。

类图

实例


  1. php
  2. abstract class Flyweight { // 抽象享元角色
  3.    abstract public function operation($state);
  4. }
  5. class ConcreteFlyweight extends Flyweight { // 具体享元角色
  6.    private $_intrinsicState = null;
  7.    public function __construct($state) {
  8.        $this->_intrinsicState = $state;
  9.    }
  10.    public function operation($state) {}
  11. }
  12. class UnsharedConcreteFlyweight extends Flyweight { // 不共享的具体享元,客户端直接调用
  13.    private $_intrinsicState = null;
  14.    public function __construct($state) {
  15.        $this->_intrinsicState = $state;
  16.    }
  17.    public function operation($state) {}
  18. }
  19. class FlyweightFactory { // 享元工厂角色
  20.    private $_flyweights;
  21.    public function __construct() {
  22.        $this->_flyweights = array();
  23.    }
  24.    public function getFlyweigth($state) {
  25.        if (isset($this->_flyweights[$state])) {
  26.            return $this->_flyweights[$state];
  27.        } else {
  28.            return $this->_flyweights[$state] = new ConcreteFlyweight($state);
  29.        }
  30.    }
  31. }
  32. // client
  33. $flyweightFactory = new FlyweightFactory();
  34. $flyweight = $flyweightFactory->getFlyweigth('state A');
  35. $flyweight->operation('other state A');
  36. $flyweight = $flyweightFactory->getFlyweigth('state B');
  37. $flyweight->operation('other state B');
  38. // 不共享的对象,单独调用
  39. $uflyweight = new UnsharedConcreteFlyweight('state A');
  40. $uflyweight->operation('other state A');
  41. ?>

优缺点

优点

  • Flyweight模式可以大幅度地降低内存中对象的数量。

缺点

  • Flyweight模式使得系统更加复杂
  • Flyweigth模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长

参考

  1. Wikipedia: Flyweight pattern
  2. Wikipedia: 享元模式
  3. PHP设计模式笔记:使用PHP实现享元模式

观察者模式(Observer pattern)

观察者模式是一种行为型模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

又称为发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听(Source-Listener)模式、或从属者(Dependents)模式

主要角色

  • 抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。抽象主题提供了增加和删除观察者对象的接口。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
  • 具体主题(ConcreteSubject)角色:存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
  • 具体观察者(ConcretedObserver)角色:存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。

适用性

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
  • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。

类图

实例


  1. php
  2. interface Subject { // 抽象主题角色
  3.    public function attach(Observer $observer); // 增加一个新的观察者对象
  4.    public function detach(Observer $observer); // 删除一个已注册过的观察者对象
  5.    public function notifyObservers(); // 通知所有注册过的观察者对象
  6. }
  7. class ConcreteSubject implements Subject { // 具体主题角色
  8.    private $_observers;
  9.    public function __construct() { $this->_observers = array(); }
  10.    public function attach(Observer $observer) {
  11.        return array_push($this->_observers, $observer);
  12.    }
  13.    public function detach(Observer $observer) {
  14.        $index = array_search($observer, $this->_observers);
  15.        if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
  16.            return FALSE;
  17.        }
  18.        unset($this->_observers[$index]);
  19.        return TRUE;
  20.    }
  21.    public function notifyObservers() {
  22.        if (!is_array($this->_observers)) { return FALSE; }
  23.        foreach ($this->_observers as $observer) {
  24.            $observer->update();
  25.        }
  26.        return TRUE;
  27.    }
  28. }
  29. interface Observer { // 抽象观察者角色
  30.    public function update(); // 更新方法
  31. }
  32. class ConcreteObserver implements Observer {
  33.    private $_name;
  34.    public function __construct($name) { $this->_name = $name; }
  35.    public function update() {}
  36. }
  37. $subject = new ConcreteSubject();
  38. /* 添加第一个观察者 */
  39. $observer1 = new ConcreteObserver('Mac');
  40. $subject->attach($observer1);
  41. $subject->notifyObservers(); // 主题变化,通知观察者
  42. /* 添加第二个观察者 */
  43. $observer2 = new ConcreteObserver('Win');
  44. $subject->attach($observer2);
  45. $subject->notifyObservers();
  46. $subject->detach($observer1);
  47. $subject->notifyObservers();
  48. ?>

优缺点

优点

  • 观察者和主题之间的耦合度较小。
  • 支持广播通信。

缺点

  • 由于观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。这可能会引起意外的更新。

参考

  1. Wikipedia: Observer pattern
  2. Wikipedia: 观察者模式
  3. PHP设计模式笔记:使用PHP实现观察者模式

原型模式(Prototype pattern)

原型模式是一种创建者模式,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。

原型模式中主要角色

  • 抽象原型(Prototype)角色:声明一个克隆自己的接口
  • 具体原型(Concrete Prototype)角色:实现一个克隆自己的操作

适用性

  • 当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式
  • 当要实例化的类是在运行时刻指定时,例如动态加载
  • 为了避免创建一个与产品类层次平等的工厂类层次时;
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些

类图

实例


  1. php
  2. interface Prototype { public function copy(); }
  3. class ConcretePrototype implements Prototype{
  4.    private  $_name;
  5.    public function __construct($name) { $this->_name = $name; }
  6.    public function copy() { return clone $this;}
  7. }
  8. class Demo {}
  9. // client
  10. $demo = new Demo();
  11. $object1 = new ConcretePrototype($demo);
  12. $object2 = $object1->copy();
  13. ?>

优缺点

优点

  • 可以在运行时刻增加和删除产品
  • 可以改变值以指定新对象
  • 可以改变结构以指定新对象
  • 减少子类的构造
  • 用类动态配置应用

缺点

Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

参考

  1. Wikipedia: Prototype pattern
  2. Wikipedia: 原型模式
  3. PHP设计模式笔记:使用PHP实现原型模式

代理模式(Proxy pattern)

代理模式是一种结构型模式,它可以为其他对象提供一种代理以控制对这个对象的访问。

主要角色

  • 抽象主题角色(Subject):它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口,这样就可以在使用真实主题角色的地方使用代理主题角色。
  • 真实主题角色(RealSubject):隐藏在代理角色后面的真实对象。
  • 代理主题角色(ProxySubject):它的作用是代理真实主题,在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色,保持接口的统一。它可以控制对真实主题的存取,并可能负责创建和删除真实对象。代理角色并不是简单的转发,通常在将调用传递给真实对象之前或之后执行某些操作,当然你也可以只是简单的转发。 与适配器模式相比:适配器模式是为了改变对象的接口,而代理模式并不能改变所代理对象的接口。

适用性

  • 为一些复杂的子系统提供一组接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用门面模式定义系统的每一层的接口

类图

实例


  1. php
  2. abstract class Subject { // 抽象主题角色
  3.    abstract public function action();
  4. }
  5. class RealSubject extends Subject { // 真实主题角色
  6.    public function __construct() {}
  7.    public function action() {}
  8. }
  9. class ProxySubject extends Subject { // 代理主题角色
  10.    private $_real_subject = NULL;
  11.    public function __construct() {}
  12.    public function action() {
  13.        $this->_beforeAction();
  14.        if (is_null($this->_real_subject)) {
  15.            $this->_real_subject = new RealSubject();
  16.        }
  17.        $this->_real_subject->action();
  18.        $this->_afterAction();
  19.    }
  20.    private function _beforeAction() {}
  21.    private function _afterAction() {}
  22. }
  23. // client
  24. $subject = new ProxySubject();
  25. $subject->action();
  26. ?>

参考

  1. Wikipedia: Proxy pattern
  2. Wikipedia: 代理模式
  3. 代理模式(Proxy)和PHP的反射功能

策略模式(Strategy pattern)

策略模式是一种行为型模式,它定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式可以使算法可独立于使用它的客户而变化。

主要角色

  • 抽象策略(Strategy)角色:定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现。Context使用这个接口来调用其ConcreteStrategy定义的算法
  • 具体策略(ConcreteStrategy)角色:以Strategy接口实现某具体算法
  • 环境(Context)角色:持有一个Strategy类的引用,用一个ConcreteStrategy对象来配置

适用性

  • 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法
  • 需要使用一个算法的不同变体。
  • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构
  • 一个类定义了多种行为,并且 这些行为在这个类的操作中以多个形式出现。将相关的条件分支移和它们各自的Strategy类中以代替这些条件语句

类图

实例


  1. php
  2. interface

你可能感兴趣的:(PHP)