设计模式(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框架,这个过程会让你感觉到自己缺失的部分。
设计模式的优点:
在《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software) 这本书中,作者把设计模式分了三大类:
创建型模式是为了解决创建对象时候遇到的问题。因为基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式有两个主导思想:一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合方式。
最常见的五种创建型模式如下:
结构型模式是通过定义一个简单的方法来实现和了解实体间关系,从而简化设计。
行为型模式用来识别对象之间的常用交流模式并加以实现,使得交流变得更加灵活。
这里有一张各个模式关系图,可以在了解各个模式以后梳理一下
建造者模式是一种创建型模式,它可以让一个产品的内部表象和和产品的生产过程分离开,从而可以生成具有不同内部表象的产品。
php
class Product { // 产品本身
private $_parts;
public function __construct() { $this->_parts = array(); }
public function add($part) { return array_push($this->_parts, $part); }
}
abstract class Builder { // 建造者抽象类
public abstract function buildPart1();
public abstract function buildPart2();
public abstract function getResult();
}
class ConcreteBuilder extends Builder { // 具体建造者
private $_product;
public function __construct() { $this->_product = new Product(); }
public function buildPart1() { $this->_product->add("Part1"); }
public function buildPart2() { $this->_product->add("Part2"); }
public function getResult() { return $this->_product; }
}
class Director {
public function __construct(Builder $builder) {
$builder->buildPart1();
$builder->buildPart2();
}
}
// client
$buidler = new ConcreteBuilder();
$director = new Director($buidler);
$product = $buidler->getResult();
?>
建造者模式可以很好的将一个对象的实现与相关的“业务”逻辑分离开来,从而可以在不改变事件逻辑的前提下,使增加(或改变)实现变得非常容易。
建造者接口的修改会导致所有执行类的修改。
抽象工厂模式是一种创建型模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在。
实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
Singleton定义一个getInstance操作,允许客户访问它唯一的实例。
这个例子也简单,就像我有6个老婆(快醒醒!),她们在喊”老公”的时候都是指我。不管什么时候,喊老公擦地,做饭,洗衣服都是指同一个人,PHP不编写多线程,所以不存在抢占问题,如果换别的语言编写,一定得考虑到抢占问题!老公是不可以边擦地边做饭的!
php
public class Singleton {
private static $_instance = NULL;
// 私有构造方法
private function __construct() {}
public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new Singleton();
}
return self::$_instance;
}
// 防止克隆实例
public function __clone(){
die('Clone is not allowed.' . E_USER_ERROR);
}
}
?>
在此实例中,Singleton禁止了克隆及外部初始化,使得此类只可以通过getInstance()
方法来获得实例,而这个实例只会在第一次使用时创建,以后每次都获得同一实例。
#### 优点
- 对唯一实例的受控访问
- 缩小命名空间 单例模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染命名空间
- 允许对操作和表示的精华,单例类可以有子类。而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
- 允许可变数目的实例(多例模式)
- 比类操作更灵活
单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
适配器模式是一种结构型模式,它将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类别自己的接口包裹在一个已存在的类中。
1、你想使用一个已经存在的类,而它的接口不符合你的需求
2、你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作
3、你想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口(仅限于对象适配器)
类适配器:Adapter与Adaptee是继承关系
对象适配器:Adapter与Adaptee是委托关系
php
interface Target {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee { // 源角色
public function sampleMethod1() {}
}
class Adapter extends Adaptee implements Target { // 适配后角色
public function sampleMethod2() {}
}
// client
$adapter = new Adapter();
$adapter->sampleMethod1();
$adapter->sampleMethod2();
?>
php
interface Target {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee {
public function sampleMethod1() {}
}
class Adapter implements Target {
private $_adaptee;
public function __construct(Adaptee $adaptee) {
$this->_adaptee = $adaptee;
}
public function sampleMethod1() { $this->_adaptee->sampleMethod1(); }
public function sampleMethod2() {}
}
$adaptee = new Adaptee();
$adapter = new Adapter($adaptee);
$adapter->sampleMethod1();
$adapter->sampleMethod2();
?>
桥接模式是一种结构型模式,它是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。
php
abstract class Abstraction { // 抽象化角色,抽象化给出的定义,并保存一个对实现化对象的引用。
protected $imp; // 对实现化对象的引用
public function operation() {
$this->imp->operationImp();
}
}
class RefinedAbstraction extends Abstraction { // 修正抽象化角色, 扩展抽象化角色,改变和修正父类对抽象化的定义。
public function __construct(Implementor $imp) {
$this->imp = $imp;
}
public function operation() { $this->imp->operationImp(); }
}
abstract class Implementor { // 实现化角色, 给出实现化角色的接口,但不给出具体的实现。
abstract public function operationImp();
}
class ConcreteImplementorA extends Implementor { // 具体化角色A
public function operationImp() {}
}
class ConcreteImplementorB extends Implementor { // 具体化角色B
public function operationImp() {}
}
// client
$abstraction = new RefinedAbstraction(new ConcreteImplementorA());
$abstraction->operation();
$abstraction = new RefinedAbstraction(new ConcreteImplementorB());
$abstraction->operation();
?>
合成模式是一种结构型模式,它将对象组合成树形结构以表示”部分-整体”的层次结构。Composite使用户对单个对象和组合对象的使用具有一致性。
Composite变化的是一个对象的结构和组成。
在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的。因为树叶类型的对象根本就没有管理子类的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。编译通不过,就不会出现运行时期错误。这样的缺点是不够透明,因为树叶类和合成类将具有不同的接口。
php
interface Component {
public function getComposite(); //返回自己的实例
public function operation();
}
class Composite implements Component { // 树枝组件角色
private $_composites;
public function __construct() { $this->_composites = array(); }
public function getComposite() { return $this; }
public function operation() {
foreach ($this->_composites as $composite) {
$composite->operation();
}
}
public function add(Component $component) { //聚集管理方法 添加一个子对象
$this->_composites[] = $component;
}
public function remove(Component $component) { // 聚集管理方法 删除一个子对象
foreach ($this->_composites as $key => $row) {
if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
}
return FALSE;
}
public function getChild() { // 聚集管理方法 返回所有的子对象
return $this->_composites;
}
}
class Leaf implements Component {
private $_name;
public function __construct($name) { $this->_name = $name; }
public function operation() {}
public function getComposite() {return null;}
}
// client
$leaf1 = new Leaf('first');
$leaf2 = new Leaf('second');
$composite = new Composite();
$composite->add($leaf1);
$composite->add($leaf2);
$composite->operation();
$composite->remove($leaf2);
$composite->operation();
?>
在Composite类里面声明所有的用来管理子类对象的方法。这样做的是好处是所有的组件类都有相同的接口。在客户端看来,树叶类和合成类对象的区别起码在接口层次上消失了,客户端可以同等的对待所有的对象。这就是透明形式的合成模式,缺点就是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此调用其添加或删除方法就没有意义了,这在编译期间是不会出错的,而只会在运行时期才会出错。
php
interface Component { // 抽象组件角色
public function getComposite(); // 返回自己的实例
public function operation(); // 示例方法
public function add(Component $component); // 聚集管理方法,添加一个子对象
public function remove(Component $component); // 聚集管理方法 删除一个子对象
public function getChild(); // 聚集管理方法 返回所有的子对象
}
class Composite implements Component { // 树枝组件角色
private $_composites;
public function __construct() { $this->_composites = array(); }
public function getComposite() { return $this; }
public function operation() { // 示例方法,调用各个子对象的operation方法
foreach ($this->_composites as $composite) {
$composite->operation();
}
}
public function add(Component $component) { // 聚集管理方法 添加一个子对象
$this->_composites[] = $component;
}
public function remove(Component $component) { // 聚集管理方法 删除一个子对象
foreach ($this->_composites as $key => $row) {
if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
}
return FALSE;
}
public function getChild() { // 聚集管理方法 返回所有的子对象
return $this->_composites;
}
}
class Leaf implements Component {
private $_name;
public function __construct($name) {$this->_name = $name;}
public function operation() {}
public function getComposite() { return null; }
public function add(Component $component) { return FALSE; }
public function remove(Component $component) { return FALSE; }
public function getChild() { return null; }
}
// client
$leaf1 = new Leaf('first');
$leaf2 = new Leaf('second');
$composite = new Composite();
$composite->add($leaf1);
$composite->add($leaf2);
$composite->operation();
$composite->remove($leaf2);
$composite->operation();
?>
#### 优点
装饰器模式是一种结构型模式,它动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活【GOF95】
装饰模式是以对客户透明的方式动态地给一个对象附加上更多的职责。这也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
php
interface Component {
public function operation();
}
abstract class Decorator implements Component{ // 装饰角色
protected $_component;
public function __construct(Component $component) {
$this->_component = $component;
}
public function operation() {
$this->_component->operation();
}
}
class ConcreteDecoratorA extends Decorator { // 具体装饰类A
public function __construct(Component $component) {
parent::__construct($component);
}
public function operation() {
parent::operation(); // 调用装饰类的操作
$this->addedOperationA(); // 新增加的操作
}
public function addedOperationA() {}
}
class ConcreteDecoratorB extends Decorator { // 具体装饰类B
public function __construct(Component $component) {
parent::__construct($component);
}
public function operation() {
parent::operation();
$this->addedOperationB();
}
public function addedOperationB() {}
}
class ConcreteComponent implements Component{
public function operation() {}
}
// clients
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);
$decoratorA->operation();
$decoratorB->operation();
?>
门面模式是一种结构型模式,它为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层次的接口,使得子系统更加容易使用。
php
class Camera {
public function turnOn() {}
public function turnOff() {}
public function rotate($degrees) {}
}
class Light {
public function turnOn() {}
public function turnOff() {}
public function changeBulb() {}
}
class Sensor {
public function activate() {}
public function deactivate() {}
public function trigger() {}
}
class Alarm {
public function activate() {}
public function deactivate() {}
public function ring() {}
public function stopRing() {}
}
class SecurityFacade {
private $_camera1, $_camera2;
private $_light1, $_light2, $_light3;
private $_sensor;
private $_alarm;
public function __construct() {
$this->_camera1 = new Camera();
$this->_camera2 = new Camera();
$this->_light1 = new Light();
$this->_light2 = new Light();
$this->_light3 = new Light();
$this->_sensor = new Sensor();
$this->_alarm = new Alarm();
}
public function activate() {
$this->_camera1->turnOn();
$this->_camera2->turnOn();
$this->_light1->turnOn();
$this->_light2->turnOn();
$this->_light3->turnOn();
$this->_sensor->activate();
$this->_alarm->activate();
}
public function deactivate() {
$this->_camera1->turnOff();
$this->_camera2->turnOff();
$this->_light1->turnOff();
$this->_light2->turnOff();
$this->_light3->turnOff();
$this->_sensor->deactivate();
$this->_alarm->deactivate();
}
}
//client
$security = new SecurityFacade();
$security->activate();
?>
享元模式是一种结构型模式,它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
php
abstract class Flyweight { // 抽象享元角色
abstract public function operation($state);
}
class ConcreteFlyweight extends Flyweight { // 具体享元角色
private $_intrinsicState = null;
public function __construct($state) {
$this->_intrinsicState = $state;
}
public function operation($state) {}
}
class UnsharedConcreteFlyweight extends Flyweight { // 不共享的具体享元,客户端直接调用
private $_intrinsicState = null;
public function __construct($state) {
$this->_intrinsicState = $state;
}
public function operation($state) {}
}
class FlyweightFactory { // 享元工厂角色
private $_flyweights;
public function __construct() {
$this->_flyweights = array();
}
public function getFlyweigth($state) {
if (isset($this->_flyweights[$state])) {
return $this->_flyweights[$state];
} else {
return $this->_flyweights[$state] = new ConcreteFlyweight($state);
}
}
}
// client
$flyweightFactory = new FlyweightFactory();
$flyweight = $flyweightFactory->getFlyweigth('state A');
$flyweight->operation('other state A');
$flyweight = $flyweightFactory->getFlyweigth('state B');
$flyweight->operation('other state B');
// 不共享的对象,单独调用
$uflyweight = new UnsharedConcreteFlyweight('state A');
$uflyweight->operation('other state A');
?>
观察者模式是一种行为型模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
又称为发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听(Source-Listener)模式、或从属者(Dependents)模式
php
interface Subject { // 抽象主题角色
public function attach(Observer $observer); // 增加一个新的观察者对象
public function detach(Observer $observer); // 删除一个已注册过的观察者对象
public function notifyObservers(); // 通知所有注册过的观察者对象
}
class ConcreteSubject implements Subject { // 具体主题角色
private $_observers;
public function __construct() { $this->_observers = array(); }
public function attach(Observer $observer) {
return array_push($this->_observers, $observer);
}
public function detach(Observer $observer) {
$index = array_search($observer, $this->_observers);
if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
return FALSE;
}
unset($this->_observers[$index]);
return TRUE;
}
public function notifyObservers() {
if (!is_array($this->_observers)) { return FALSE; }
foreach ($this->_observers as $observer) {
$observer->update();
}
return TRUE;
}
}
interface Observer { // 抽象观察者角色
public function update(); // 更新方法
}
class ConcreteObserver implements Observer {
private $_name;
public function __construct($name) { $this->_name = $name; }
public function update() {}
}
$subject = new ConcreteSubject();
/* 添加第一个观察者 */
$observer1 = new ConcreteObserver('Mac');
$subject->attach($observer1);
$subject->notifyObservers(); // 主题变化,通知观察者
/* 添加第二个观察者 */
$observer2 = new ConcreteObserver('Win');
$subject->attach($observer2);
$subject->notifyObservers();
$subject->detach($observer1);
$subject->notifyObservers();
?>
原型模式是一种创建者模式,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。
php
interface Prototype { public function copy(); }
class ConcretePrototype implements Prototype{
private $_name;
public function __construct($name) { $this->_name = $name; }
public function copy() { return clone $this;}
}
class Demo {}
// client
$demo = new Demo();
$object1 = new ConcretePrototype($demo);
$object2 = $object1->copy();
?>
Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
代理模式是一种结构型模式,它可以为其他对象提供一种代理以控制对这个对象的访问。
php
abstract class Subject { // 抽象主题角色
abstract public function action();
}
class RealSubject extends Subject { // 真实主题角色
public function __construct() {}
public function action() {}
}
class ProxySubject extends Subject { // 代理主题角色
private $_real_subject = NULL;
public function __construct() {}
public function action() {
$this->_beforeAction();
if (is_null($this->_real_subject)) {
$this->_real_subject = new RealSubject();
}
$this->_real_subject->action();
$this->_afterAction();
}
private function _beforeAction() {}
private function _afterAction() {}
}
// client
$subject = new ProxySubject();
$subject->action();
?>
策略模式是一种行为型模式,它定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式可以使算法可独立于使用它的客户而变化。
php
interface