设计模式 php 创建型模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
> 如果你对设计模式完全没有感觉,那么去好好写一个类库,或者一个简单的MVC框架,这个过程会让你感觉到自己缺失的部分。
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 建造者模式
- 原型模式
工厂模式
适用性
工厂方法模式就像我们去麦当劳买汉堡,我们只要找到服务员,让他帮我们拿来汉堡即可。其中具体某个服务员就像具体工厂,他继承了服务员应有的服务。汉堡在到手以前属于抽象产品,而我们拿到的汉堡就属于具体产品。
- 创建对象需要大量重复的代码(例如创建一个MySQL操作类,需要配置很多选项,这些都可以在工厂方法中进行)。
- 创建对象需要访问某些信息,而这些信息不应该包含在复合类中。
- 创建对象的生命周期必须集中管理,以保证在整个程序中具有一致的行为。
实例
class Button{/* ...*/}
class WinButton extends Button{/* ...*/}
class MacButton extends Button{/* ...*/}
interface ButtonFactory{
public function createButton($type);
}
class MyButtonFactory implements ButtonFactory{
// 实现工厂方法
public function createButton($type){
switch($type){
case 'Mac':
return new MacButton();
case 'Win':
return new WinButton();
}
}
}
抽象工厂模式
这个和工厂方法模式类似,我们不再只要一个汉堡,可能是4个汉堡2个鸡翅,我们还是对服务员说,服务员属于具体工厂,抽象产品就是麦当劳可卖的食物,具体产品是我们跟服务员要的食物。
适用性
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 需要强调一系列相关的产品对象的设计以便进行联合使用时。
- 提供一个产品类库,而只想显示它们的接口而不是实现时。
实例
class Button{}
class Border{}
class MacButton extends Button{}
class WinButton extends Button{}
class MacBorder extends Border{}
class WinBorder extends Border{}
interface AbstractFactory {
public function CreateButton();
public function CreateBorder();
}
class MacFactory implements AbstractFactory{
public function CreateButton(){ return new MacButton(); }
public function CreateBorder(){ return new MacBorder(); }
}
class WinFactory implements AbstractFactory{
public function CreateButton(){ return new WinButton(); }
public function CreateBorder(){ return new WinBorder(); }
}
单例模式
单例模式顾名思义,就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
为什么要使用单例模式
PHP的应用主要在于数据库应用,一个应用中会存在大量的数据库操作,在使用面向对象的方式开发时,如果使用单例模式,则可以避免大量的new 操作消耗的资源,还可以减少数据库连接,这样就不容易出现 too many connections情况。
如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现, 这个可以参看zend Framework的FrontController部分。
在一次页面请求中,便于进行调试,因为所有的代码(例如数据库操作类db)都集中在一个类中,我们可以在类中设置钩子,输出日志,从而避免到处var_dump, echo。
代码
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()方法来获得实例,而这个实例只会在第一次使用时创建,以后每次都获得同一实例。
优缺点
优点
- 对唯一实例的受控访问
- 缩小命名空间 单例模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染命名空间
- 允许对操作和表示的精华,单例类可以有子类。而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
- 允许可变数目的实例(多例模式)
- 比类操作更灵活
缺点
- 单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
建造者模式
建造者模式的好处就是保证了流程不会变化,流程即不会增加、也不会遗漏或者产生流程次序错误,这是非常重要的。我们熟知的楼歪歪事件,官方的解释就是由于先建立楼房后,再建设停车场造成的,这是典型的建造次序错乱。。
适用性
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
主要角色
- 抽象建造者(Builder)角色:定义抽象接口,规范产品各个部分的建造,必须包括建造方法和返回方法。
- 具体建造者(Concrete)角色:实现抽象建造者接口。应用程序最终根据此角色中实现的业务逻辑创造产品。
- 导演者(Director)角色:调用具体的建造者角色创造产品。
- 产品(Product)角色:在导演者的指导下所创建的复杂对象。
实例
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();
优缺点
优点
建造者模式可以很好的将一个对象的实现与相关的“业务”逻辑分离开来,从而可以在不改变事件逻辑的前提下,使增加(或改变)实现变得非常容易。
缺点
建造者接口的修改会导致所有执行类的修改。
原型模式
原型模式是一种创建者模式,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。
主要角色
- 具体原型(Concrete Prototype)角色:实现一个克隆自己的操作
适用性 - 抽象原型(Prototype)角色:声明一个克隆自己的接口
适用性
- 当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式
- 当要实例化的类是在运行时刻指定时,例如动态加载
- 为了避免创建一个与产品类层次平等的工厂类层次时;
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些
实例
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模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。