PhpDesignPatterns 【PHP 中的設計模式】
一、 Introduction【介紹】
設計模式:提供了一種廣泛的可重用的方式來解決我們日常編程中常常遇見的問題。設計模式並不一定就是一個類庫或者第三方框架,它們更多的表現爲一種思想並且廣泛地應用在系統中。它們也表現爲一種模式或者模板,可以在多個不同的場景下用於解決問題。設計模式可以用於加速開發,並且將很多大的想法或者設計以一種簡單地方式實現。當然,雖然設計模式在開發中很有作用,但是千萬要避免在不適當的場景誤用它們。
二、 Category【分類】
根據目的和範圍,設計模式可以分爲五類。
按照目的分爲:創建設計模式,結構設計模式,以及行爲設計模式。
按照範圍分爲:類的設計模式,以及對象設計模式。
1. 按照目的分,目前常見的設計模式主要有23種,根據使用目標的不同可以分爲以下三大類:
創建設計模式(Creational Patterns)(5種):用於創建對象時的設計模式。更具體一點,初始化對象流程的設計模式。當程序日益複雜時,需要更加靈活地創建對象,同時減少創建時的依賴。而創建設計模式就是解決此問題的一類設計模式。
- 單例模式【Singleton】
- 工廠模式【Factory】
- 抽象工廠模式【AbstractFactory】
- 建造者模式【Builder】
- 原型模式【Prototype】
結構設計模式(Structural Patterns)(7種):用於繼承和接口時的設計模式。結構設計模式用於新類的函數方法設計,減少不必要的類定義,減少代碼的冗餘。
- 適配器模式【Adapter】
- 橋接模式【Bridge】
- 合成模式【Composite】
- 裝飾器模式【Decorator】
- 門面模式【Facade】
- 代理模式【Proxy】
- 享元模式【Flyweight】
行爲模式(Behavioral Patterns)(11種):用於方法實現以及對應算法的設計模式,同時也是最複雜的設計模式。行爲設計模式不僅僅用於定義類的函數行爲,同時也用於不同類之間的協議、通信。
- 策略模式【Strategy】
- 模板方法模式【TemplateMethod】
- 觀察者模式【Observer】
- 迭代器模式【Iterator】
- 責任鏈模式【ResponsibilityChain】
- 命令模式【Command】
- 備忘錄模式【Memento】
- 狀態模式【State】
- 訪問者模式【Visitor】
- 中介者模式【Mediator】
- 解釋器模式【Interpreter】
2.按照範圍分爲:類的設計模式,以及對象設計模式
類的設計模式(Class patterns):用於類的具體實現的設計模式。包含了如何設計和定義類,以及父類和子類的設計模式。
對象設計模式(Object patterns): 用於對象的設計模式。與類的設計模式不同,對象設計模式主要用於運行期對象的狀態改變、動態行爲變更等。
三、 DesignPatternsPrinciple【設計模式原則】
設計模式六大原則
- 開放封閉原則:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。
- 里氏替換原則:所有引用基類的地方必須能透明地使用其子類的對象.
- 依賴倒置原則:高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。
- 單一職責原則:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。
- 接口隔離原則:客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。
- 迪米特法則:一個對象應該對其他對象保持最少的瞭解。
四、 Realization【設計模式實現】
Creational Patterns(創建設計模式)
1. Singleton(單例模式)
- Singleton(單例模式):單例模式是最常見的模式之一,在Web應用的開發中,常常用於允許在運行時爲某個特定的類創建僅有一個可訪問的實例。
<?php
/**
* Singleton class[單例模式]
* @author ITYangs<[email protected]>
*/
final class Mysql
{
/**
*
* @var self[該屬性用來保存實例]
*/
private static $instance;
/**
*
* @var mixed
*/
public $mix;
/**
* Return self instance[創建一個用來實例化對象的方法]
*
* @return self
*/
public static function getInstance()
{
if (! (self::$instance instanceof self)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 構造函數爲private,防止創建對象
*/
private function __construct()
{}
/**
* 防止對象被複制
*/
private function __clone()
{
trigger_error('Clone is not allowed !');
}
}
// @test
$firstMysql = Mysql::getInstance();
$secondMysql = Mysql::getInstance();
$firstMysql->mix = 'ityangs_one';
$secondMysql->mix = 'ityangs_two';
print_r($firstMysql->mix);
// 輸出: ityangs_two
print_r($secondMysql->mix);
// 輸出: ityangs_two
在很多情況下,需要爲系統中的多個類創建單例的構造方式,這樣,可以建立一個通用的抽象父工廠方法:
<?php
/**
* Singleton class[單例模式:多個類創建單例的構造方式]
* @author ITYangs<[email protected]>
*/
abstract class FactoryAbstract {
protected static $instances = array();
public static function getInstance() {
$className = self::getClassName();
if (!(self::$instances[$className] instanceof $className)) {
self::$instances[$className] = new $className();
}
return self::$instances[$className];
}
public static function removeInstance() {
$className = self::getClassName();
if (array_key_exists($className, self::$instances)) {
unset(self::$instances[$className]);
}
}
final protected static function getClassName() {
return get_called_class();
}
protected function __construct() { }
final protected function __clone() { }
}
abstract class Factory extends FactoryAbstract {
final public static function getInstance() {
return parent::getInstance();
}
final public static function removeInstance() {
parent::removeInstance();
}
}
// @test
class FirstProduct extends Factory {
public $a = [];
}
class SecondProduct extends FirstProduct {
}
FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 11;
SecondProduct::getInstance()->a[] = 22;
print_r(FirstProduct::getInstance()->a);
// Array ( [0] => 1 [1] => 11 )
print_r(SecondProduct::getInstance()->a);
// Array ( [0] => 2 [1] => 22 )
2. Factory(工廠模式)
工廠模式是另一種非常常用的模式,正如其名字所示:確實是對象實例的生產工廠。某些意義上,工廠模式提供了通用的方法有助於我們去獲取對象,而不需要關心其具體的內在的實現。
<?php
/**
* Factory class[工廠模式]
* @author ITYangs<[email protected]>
*/
interface SystemFactory
{
public function createSystem($type);
}
class MySystemFactory implements SystemFactory
{
// 實現工廠方法
public function createSystem($type)
{
switch ($type) {
case 'Mac':
return new MacSystem();
case 'Win':
return new WinSystem();
case 'Linux':
return new LinuxSystem();
}
}
}
class System{ /* ... */}
class WinSystem extends System{ /* ... */}
class MacSystem extends System{ /* ... */}
class LinuxSystem extends System{ /* ... */}
//創建我的系統工廠
$System_obj = new MySystemFactory();
//用我的系統工廠分別創建不同系統對象
var_dump($System_obj->createSystem('Mac'));//輸出:object(MacSystem)#2 (0) { }
var_dump($System_obj->createSystem('Win'));//輸出:object(WinSystem)#2 (0) { }
var_dump($System_obj->createSystem('Linux'));//輸出:object(LinuxSystem)#2 (0) { }
3. AbstractFactory(抽象工廠模式)
有些情況下我們需要根據不同的選擇邏輯提供不同的構造工廠,而對於多個工廠而言需要一個統一的抽象工廠:
<?php
class System{}
class Soft{}
class MacSystem extends System{}
class MacSoft extends Soft{}
class WinSystem extends System{}
class WinSoft extends Soft{}
/**
* AbstractFactory class[抽象工廠模式]
* @author ITYangs<[email protected]>
*/
interface AbstractFactory {
public function CreateSystem();
public function CreateSoft();
}
class MacFactory implements AbstractFactory{
public function CreateSystem(){ return new MacSystem(); }
public function CreateSoft(){ return new MacSoft(); }
}
class WinFactory implements AbstractFactory{
public function CreateSystem(){ return new WinSystem(); }
public function CreateSoft(){ return new WinSoft(); }
}
//@test:創建工廠->用該工廠生產對應的對象
//創建MacFactory工廠
$MacFactory_obj = new MacFactory();
//用MacFactory工廠分別創建不同對象
var_dump($MacFactory_obj->CreateSystem());//輸出:object(MacSystem)#2 (0) { }
var_dump($MacFactory_obj->CreateSoft());// 輸出:object(MacSoft)#2 (0) { }
//創建WinFactory
$WinFactory_obj = new WinFactory();
//用WinFactory工廠分別創建不同對象
var_dump($WinFactory_obj->CreateSystem());//輸出:object(WinSystem)#3 (0) { }
var_dump($WinFactory_obj->CreateSoft());//輸出:object(WinSoft)#3 (0) { }
4. Builder(建造者模式)
建造者模式主要在於創建一些複雜的對象。將一個複雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示的設計模式;
結構圖:
<?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();
echo "<pre>";
var_dump($product);
echo "</pre>";
/*輸出: object(Product)#2 (1) {
["_parts":"Product":private]=>
array(2) {
[0]=>string(5) "Part1"
[1]=>string(5) "Part2"
}
} */
?>
5. Prototype(原型模式)
有時候,部分對象需要被初始化多次。而特別是在如果初始化需要耗費大量時間與資源的時候進行預初始化並且存儲下這些對象,就會用到原型模式:
<?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 Test {}
// client
$object1 = new ConcretePrototype(new Test());
var_dump($object1);//輸出:object(ConcretePrototype)#1 (1) { ["_name":"ConcretePrototype":private]=> object(Test)#2 (0) { } }
$object2 = $object1->copy();
var_dump($object2);//輸出:object(ConcretePrototype)#3 (1) { ["_name":"ConcretePrototype":private]=> object(Test)#2 (0) { } }
?>
Structural Patterns(結構設計模式)
6. Adapter(適配器模式)
這種模式允許使用不同的接口重構某個類,可以允許使用不同的調用方式進行調用:
<?php
/**
* 第一種方式:對象適配器
*/
interface Target {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee {
public function sampleMethod1() {
echo '++++++++';
}
}
class Adapter implements Target {
private $_adaptee;
public function __construct(Adaptee $adaptee) {
$this->_adaptee = $adaptee;
}
public function sampleMethod1() {
$this->_adaptee->sampleMethod1();
}
public function sampleMethod2() {
echo '————————';
}
}
$adapter = new Adapter(new Adaptee());
$adapter->sampleMethod1();//輸出:++++++++
$adapter->sampleMethod2();//輸出:————————
/**
* 第二種方式:類適配器
*/
interface Target2 {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee2 { // 源角色
public function sampleMethod1() {echo '++++++++';}
}
class Adapter2 extends Adaptee2 implements Target2 { // 適配后角色
public function sampleMethod2() {echo '————————';}
}
$adapter = new Adapter2();
$adapter->sampleMethod1();//輸出:++++++++
$adapter->sampleMethod2();//輸出:————————
?>
7. Bridge(橋接模式)
將抽象部分與它的實現部分分離,使他們都可以獨立的變抽象與它的實現分離,即抽象類和它的派生類用來實現自己的對象
橋接與適配器模式的關係(適配器模式上面已講解):
橋接屬於聚合關係,兩者關聯 但不繼承
適配器屬於組合關係,適配者需要繼承源
聚合關係:A對象可以包含B對象 但B對象不是A對象的一部分
<?php
/**
*
*實現化角色, 給出實現化角色的接口,但不給出具體的實現。
*/
abstract class Implementor {
abstract public function operationImp();
}
class ConcreteImplementorA extends Implementor { // 具體化角色A
public function operationImp() {echo "A";}
}
class ConcreteImplementorB extends Implementor { // 具體化角色B
public function operationImp() {echo "B";}
}
/**
*
* 抽象化角色,抽象化給出的定義,並保存一個對實現化對象的引用
*/
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(); }
}
// client
$abstraction = new RefinedAbstraction(new ConcreteImplementorA());
$abstraction->operation();//輸出:A
$abstraction = new RefinedAbstraction(new ConcreteImplementorB());
$abstraction->operation();//輸出:B
?>
8. Composite(合成模式)
組合模式(Composite Pattern)有時候又叫做部分-整體模式,用於將對象組合成樹形結構以表示“部分-整體”的層次關係。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
常見使用場景:如樹形菜單、文件夾菜單、部門組織架構圖等。
<?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();
/**
*
*透明式合成模式
*/
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() {echo $this->_name."<br>";}
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();
?>
9. Decorator(裝飾器模式)
裝飾器模式允許我們根據運行時不同的情景動態地爲某個對象調用前後添加不同的行
<?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() {echo 'A加點醬油;';}
}
class ConcreteDecoratorB extends Decorator { // 具體裝飾類B
public function __construct(Component $component) {
parent::__construct($component);
}
public function operation() {
parent::operation();
$this->addedOperationB();
}
public function addedOperationB() {echo "B加點辣椒;";}
}
class ConcreteComponent implements Component{ //具體組件類
public function operation() {}
}
// clients
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);
$decoratorA->operation();//輸出:A加點醬油;
echo '<br>--------<br>';
$decoratorB->operation();//輸出:A加點醬油;B加點辣椒;
?>
10. Facade(門面模式)
門面模式 (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();
?>
11. Proxy(代理模式)
代理模式(Proxy)爲其他對象提供一種代理以控制對這個對象的訪問。使用代理模式創建代理對象,讓代理對象控制目標對象的訪問(目標對象可以是遠程的對象、創建開銷大的對象或需要安全控制的對象),並且可以在不改變目標對象的情況下添加一些額外的功能。
在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用,並且可以通過代理對象去掉客戶不能看到的內容和服務或者添加客戶需要的額外服務。
經典例子就是網絡代理,你想訪問 Facebook 或者 Twitter ,如何繞過 GFW?找個代理
<?
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() {
echo '在action前,我想幹點啥....';
}
private function _afterAction() {
echo '在action後,我還想幹點啥....';
}
}
// client
$subject = new ProxySubject();
$subject->action();//輸出:在action前,我想幹點啥....在action後,我還想幹點啥....
?>
12. Flyweight(享元模式)
運用共享技術有效的支持大量細粒度的對象
享元模式變化的是對象的存儲開銷
享元模式中主要角色:
抽象享元(Flyweight)角色:此角色是所有的具體享元類的超類,爲這些類規定出需要實現的公共接口。那些需要外運狀態的操作可以通過調用商業以參數形式傳入
具體享元(ConcreteFlyweight)角色:實現Flyweight接口,併爲內部狀態(如果有的話)拉回存儲空間。ConcreteFlyweight對象必須是可共享的。它所存儲的狀態必須是內部的
不共享的具體享元(UnsharedConcreteFlyweight)角色:並非所有的Flyweight子類都需要被共享。Flyweigth使共享成爲可能,但它並不強制共享
享元工廠(FlyweightFactory)角色:負責創建和管理享元角色。本角色必須保證享元對象可能被系統適當地共享
客戶端(Client)角色:本角色需要維護一個對所有享元對象的引用。本角色需要自行存儲所有享元對象的外部狀態
享元模式的優點:
Flyweight模式可以大幅度地降低內存中對象的數量
享元模式的缺點:
Flyweight模式使得系統更加複雜
Flyweight模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長
享元模式適用場景:
當一下情況成立時使用Flyweight模式:
1 一個應用程序使用了大量的對象
2 完全由於使用大量的對象,造成很大的存儲開銷
3 對象的大多數狀態都可變爲外部狀態
4 如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象
5 應用程序不依賴於對象標識
<?php
abstract class Resources{
public $resource=null;
abstract public function operate();
}
class unShareFlyWeight extends Resources{
public function __construct($resource_str) {
$this->resource = $resource_str;
}
public function operate(){
echo $this->resource."<br>";
}
}
class shareFlyWeight extends Resources{
private $resources = array();
public function get_resource($resource_str){
if(isset($this->resources[$resource_str])) {
return $this->resources[$resource_str];
}else {
return $this->resources[$resource_str] = $resource_str;
}
}
public function operate(){
foreach ($this->resources as $key => $resources) {
echo $key.":".$resources."<br>";
}
}
}
// client
$flyweight = new shareFlyWeight();
$flyweight->get_resource('a');
$flyweight->operate();
$flyweight->get_resource('b');
$flyweight->operate();
$flyweight->get_resource('c');
$flyweight->operate();
// 不共享的對象,單獨調用
$uflyweight = new unShareFlyWeight('A');
$uflyweight->operate();
$uflyweight = new unShareFlyWeight('B');
$uflyweight->operate();
/* 輸出:
a:a
a:a
b:b
a:a
b:b
c:c
A
B */
Behavioral Patterns(行爲模式)
13. Strategy(策略模式)
策略模式主要爲了讓客戶類能夠更好地使用某些算法而不需要知道其具體的實現。
<?php
interface Strategy { // 抽象策略角色,以接口實現
public function do_method(); // 算法接口
}
class ConcreteStrategyA implements Strategy { // 具體策略角色A
public function do_method() {
echo 'do method A';
}
}
class ConcreteStrategyB implements Strategy { // 具體策略角色B
public function do_method() {
echo 'do method B';
}
}
class ConcreteStrategyC implements Strategy { // 具體策略角色C
public function do_method() {
echo 'do method C';
}
}
class Question{ // 環境角色
private $_strategy;
public function __construct(Strategy $strategy) {
$this->_strategy = $strategy;
}
public function handle_question() {
$this->_strategy->do_method();
}
}
// client
$strategyA = new ConcreteStrategyA();
$question = new Question($strategyA);
$question->handle_question();//輸出do method A
$strategyB = new ConcreteStrategyB();
$question = new Question($strategyB);
$question->handle_question();//輸出do method B
$strategyC = new ConcreteStrategyC();
$question = new Question($strategyC);
$question->handle_question();//輸出do method C
?>
14. TemplateMethod(模板方法模式)
模板模式準備一個抽象類,將部分邏輯以具體方法以及具體構造形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。先制定一個頂級邏輯框架,而將邏輯的細節留給具體的子類去實現。
<?php
abstract class AbstractClass { // 抽象模板角色
public function templateMethod() { // 模板方法 調用基本方法組裝頂層邏輯
$this->primitiveOperation1();
$this->primitiveOperation2();
}
abstract protected function primitiveOperation1(); // 基本方法
abstract protected function primitiveOperation2();
}
class ConcreteClass extends AbstractClass { // 具體模板角色
protected function primitiveOperation1() {}
protected function primitiveOperation2(){}
}
$class = new ConcreteClass();
$class->templateMethod();
?>
15. Observer(觀察者模式)
某個對象可以被設置爲是可觀察的,只要通過某種方式允許其他對象註冊爲觀察者。每當被觀察的對象改變時,會發送信息給觀察者。
<?php
interface IObserver{
function onSendMsg( $sender, $args );
function getName();
}
interface IObservable{
function addObserver( $observer );
}
class UserList implements IObservable{
private $_observers = array();
public function sendMsg( $name ){
foreach( $this->_observers as $obs ){
$obs->onSendMsg( $this, $name );
}
}
public function addObserver( $observer ){
$this->_observers[]= $observer;
}
public function removeObserver($observer_name) {
foreach($this->_observers as $index => $observer) {
if ($observer->getName() === $observer_name) {
array_splice($this->_observers, $index, 1);
return;
}
}
}
}
class UserListLogger implements IObserver{
public function onSendMsg( $sender, $args ){
echo( "'$args' send to UserListLogger\n" );
}
public function getName(){
return 'UserListLogger';
}
}
class OtherObserver implements IObserver{
public function onSendMsg( $sender, $args ){
echo( "'$args' send to OtherObserver\n" );
}
public function getName(){
return 'OtherObserver';
}
}
$ul = new UserList();//被觀察者
$ul->addObserver( new UserListLogger() );//增加觀察者
$ul->addObserver( new OtherObserver() );//增加觀察者
$ul->sendMsg( "Jack" );//發送消息到觀察者
$ul->removeObserver('UserListLogger');//移除觀察者
$ul->sendMsg("hello");//發送消息到觀察者
/* 輸出:'Jack' send to UserListLogger 'Jack' send to OtherObserver 'hello' send to OtherObserver */
?>
16. Iterator(迭代器模式)
迭代器模式 (Iterator),又叫做遊標(Cursor)模式。提供一種方法訪問一個容器(Container)對象中各個元素,而又不需暴露該對象的內部細節。
當你需要訪問一個聚合對象,而且不管這些對象是什麼都需要遍歷的時候,就應該考慮使用迭代器模式。另外,當需要對聚集有多種方式遍歷時,可以考慮去使用迭代器模式。迭代器模式爲遍歷不同的聚集結構提供如開始、下一個、是否結束、當前哪一項等統一的接口。
php標準庫(SPL)中提供了迭代器接口 Iterator,要實現迭代器模式,實現該接口即可。
<?php
class sample implements Iterator {
private $_items ;
public function __construct(&$data) {
$this->_items = $data;
}
public function current() {
return current($this->_items);
}
public function next() {
next($this->_items);
}
public function key() {
return key($this->_items);
}
public function rewind() {
reset($this->_items);
}
public function valid() {
return ($this->current() !== FALSE);
}
}
// client
$data = array(1, 2, 3, 4, 5);
$sa = new sample($data);
foreach ($sa AS $key => $row) {
echo $key, ' ', $row, '<br />';
}
/* 輸出:
0 1
1 2
2 3
3 4
4 5 */
//Yii FrameWork Demo
class CMapIterator implements Iterator {
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var array list of keys in the map
*/
private $_keys;
/**
* @var mixed current key
*/
private $_key;
/**
* Constructor.
* @param array the data to be iterated through
*/
public function __construct(&$data) {
$this->_d=&$data;
$this->_keys=array_keys($data);
}
/**
* Rewinds internal array pointer.
* This method is required by the interface Iterator.
*/
public function rewind() {
$this->_key=reset($this->_keys);
}
/**
* Returns the key of the current array element.
* This method is required by the interface Iterator.
* @return mixed the key of the current array element
*/
public function key() {
return $this->_key;
}
/**
* Returns the current array element.
* This method is required by the interface Iterator.
* @return mixed the current array element
*/
public function current() {
return $this->_d[$this->_key];
}
/**
* Moves the internal pointer to the next array element.
* This method is required by the interface Iterator.
*/
public function next() {
$this->_key=next($this->_keys);
}
/**
* Returns whether there is an element at current position.
* This method is required by the interface Iterator.
* @return boolean
*/
public function valid() {
return $this->_key!==false;
}
}
$data = array('s1' => 11, 's2' => 22, 's3' => 33);
$it = new CMapIterator($data);
foreach ($it as $row) {
echo $row, '<br />';
}
/* 輸出:
11
22
33 */
?>
17. ResponsibilityChain(責任鏈模式)
這種模式有另一種稱呼:控制鏈模式。它主要由一系列對於某些命令的處理器構成,每個查詢會在處理器構成的責任鏈中傳遞,在每個交匯點由處理器判斷是否需要對它們進行響應與處理。每次的處理程序會在有處理器處理這些請求時暫停。
<?php
abstract class Responsibility { // 抽象責任角色
protected $next; // 下一個責任角色
public function setNext(Responsibility $l) {
$this->next = $l;
return $this;
}
abstract public function operate(); // 操作方法
}
class ResponsibilityA extends Responsibility {
public function __construct() {}
public function operate(){
if (false == is_null($this->next)) {
$this->next->operate();
echo 'Res_A start'."<br>";
}
}
}
class ResponsibilityB extends Responsibility {
public function __construct() {}
public function operate(){
if (false == is_null($this->next)) {
$this->next->operate();
echo 'Res_B start';
}
}
}
$res_a = new ResponsibilityA();
$res_b = new ResponsibilityB();
$res_a->setNext($res_b);
$res_a->operate();//輸出:Res_A start
?>
18. Command(命令模式)
命令模式:在軟件系統中,“行爲請求者”與“行爲實現者”通常呈現一種“緊耦合”。但在某些場合,比如要對行爲進行“記錄、撤銷/重做、事務”等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將“行爲請求者”與“行爲實現者”解耦?將一組行爲抽象爲對象,實現二者之間的鬆耦合。這就是命令模式。
角色分析:
抽象命令:定義命令的接口,聲明執行的方法。
具體命令:命令接口實現對象,是“虛”的實現;通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。
命令接收者:接收者,真正執行命令的對象。任何類都可能成爲一個接收者,只要它能夠實現命令要求實現的相應功能。
控制者:要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令對象的入口。
<?php
interface Command { // 命令角色
public function execute(); // 執行方法
}
class ConcreteCommand implements Command { // 具體命令方法
private $_receiver;
public function __construct(Receiver $receiver) {
$this->_receiver = $receiver;
}
public function execute() {
$this->_receiver->action();
}
}
class Receiver { // 接收者角色
private $_name;
public function __construct($name) {
$this->_name = $name;
}
public function action() {
echo 'receive some cmd:'.$this->_name;
}
}
class Invoker { // 請求者角色
private $_command;
public function __construct(Command $command) {
$this->_command = $command;
}
public function action() {
$this->_command->execute();
}
}
$receiver = new Receiver('hello world');
$command = new ConcreteCommand($receiver);
$invoker = new Invoker($command);
$invoker->action();//輸出:receive some cmd:hello world
?>
19. Memento(備忘錄模式)
備忘錄模式又叫做快照模式(Snapshot)或 Token 模式,備忘錄模式的用意是在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣就可以在合適的時候將該對象恢復到原先保存的狀態。
我們在編程的時候,經常需要保存對象的中間狀態,當需要的時候,可以恢復到這個狀態。比如,我們使用Eclipse進行編程時,假如編寫失誤(例如不小心誤刪除了幾行代碼),我們希望返回刪除前的狀態,便可以使用Ctrl+Z來進行返回。這時我們便可以使用備忘錄模式來實現。
UML:
備忘錄模式所涉及的角色有三個:備忘錄(Memento)角色、發起人(Originator)角色、負責人(Caretaker)角色。
這三個角色的職責分別是:
發起人:記錄當前時刻的內部狀態,負責定義哪些屬於備份範圍的狀態,負責創建和恢復備忘錄數據。
備忘錄:負責存儲發起人對象的內部狀態,在需要的時候提供發起人需要的內部狀態。
管理角色:對備忘錄進行管理,保存和提供備忘錄。
<?php
class Originator { // 發起人(Originator)角色
private $_state;
public function __construct() {
$this->_state = '';
}
public function createMemento() { // 創建備忘錄
return new Memento($this->_state);
}
public function restoreMemento(Memento $memento) { // 將發起人恢復到備忘錄對象記錄的狀態上
$this->_state = $memento->getState();
}
public function setState($state) { $this->_state = $state; }
public function getState() { return $this->_state; }
public function showState() {
echo $this->_state;echo "<br>";
}
}
class Memento { // 備忘錄(Memento)角色
private $_state;
public function __construct($state) {
$this->setState($state);
}
public function getState() { return $this->_state; }
public function setState($state) { $this->_state = $state;}
}
class Caretaker { // 負責人(Caretaker)角色
private $_memento;
public function getMemento() { return $this->_memento; }
public function setMemento(Memento $memento) { $this->_memento = $memento; }
}
// client
/* 創建目標對象 */
$org = new Originator();
$org->setState('open');
$org->showState();
/* 創建備忘 */
$memento = $org->createMemento();
/* 通過Caretaker保存此備忘 */
$caretaker = new Caretaker();
$caretaker->setMemento($memento);
/* 改變目標對象的狀態 */
$org->setState('close');
$org->showState();
$org->restoreMemento($memento);
$org->showState();
/* 改變目標對象的狀態 */
$org->setState('close');
$org->showState();
/* 還原操作 */
$org->restoreMemento($caretaker->getMemento());
$org->showState();
/* 輸出:
open
close
open
close
open */
?>
20. State(狀態模式)
狀態模式當一個對象的內在狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。狀態模式主要解決的是當控制一個對象狀態的條件表達式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把複雜的判斷邏輯簡化。
UML類圖:
角色:
上下文環境(Work):它定義了客戶程序需要的接口並維護一個具體狀態角色的實例,將與狀態相關的操作委託給當前的具體對象來處理。
抽象狀態(State):定義一個接口以封裝使用上下文環境的的一個特定狀態相關的行爲。
具體狀態(AmState):實現抽象狀態定義的接口。
<?php
interface State { // 抽象狀態角色
public function handle(Context $context); // 方法示例
}
class ConcreteStateA implements State { // 具體狀態角色A
private static $_instance = null;
private function __construct() {}
public static function getInstance() { // 靜態工廠方法,返還此類的唯一實例
if (is_null(self::$_instance)) {
self::$_instance = new ConcreteStateA();
}
return self::$_instance;
}
public function handle(Context $context) {
echo 'concrete_a'."<br>";
$context->setState(ConcreteStateB::getInstance());
}
}
class ConcreteStateB implements State { // 具體狀態角色B
private static $_instance = null;
private function __construct() {}
public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new ConcreteStateB();
}
return self::$_instance;
}
public function handle(Context $context) {
echo 'concrete_b'."<br>";
$context->setState(ConcreteStateA::getInstance());
}
}
class Context { // 環境角色
private $_state;
public function __construct() { // 默認爲stateA
$this->_state = ConcreteStateA::getInstance();
}
public function setState(State $state) {
$this->_state = $state;
}
public function request() {
$this->_state->handle($this);
}
}
// client
$context = new Context();
$context->request();
$context->request();
$context->request();
$context->request();
/* 輸出:
concrete_a
concrete_b
concrete_a
concrete_b */
?>
21. Visitor(訪問者模式)
訪問者模式是一種行爲型模式,訪問者表示一個作用於某對象結構中各元素的操作。它可以在不修改各元素類的前提下定義作用於這些元素的新操作,即動態的增加具體訪問者角色。
訪問者模式利用了雙重分派。先將訪問者傳入元素對象的Accept方法中,然後元素對象再將自己傳入訪問者,之後訪問者執行元素的相應方法。
主要角色
抽象訪問者角色(Visitor):爲該對象結構(ObjectStructure)中的每一個具體元素提供一個訪問操作接口。該操作接口的名字和參數標識了 要訪問的具體元素角色。這樣訪問者就可以通過該元素角色的特定接口直接訪問它。
具體訪問者角色(ConcreteVisitor):實現抽象訪問者角色接口中針對各個具體元素角色聲明的操作。
抽象節點(Node)角色:該接口定義一個accept操作接受具體的訪問者。
具體節點(Node)角色:實現抽象節點角色中的accept操作。
對象結構角色(ObjectStructure):這是使用訪問者模式必備的角色。它要具備以下特徵:能枚舉它的元素;可以提供一個高層的接口以允許該訪問者訪問它的元素;可以是一個複合(組合模式)或是一個集合,如一個列表或一個無序集合(在PHP中我們使用數組代替,因爲PHP中的數組本來就是一個可以放置任何類型數據的集合)
適用性
訪問者模式多用在聚集類型多樣的情況下。在普通的形式下必須判斷每個元素是屬於什麼類型然後進行相應的操作,從而誕生出冗長的條件轉移語句。而訪問者模式則可以比較好的解決這個問題。對每個元素統一調用
訪問者模式多用於被訪問的類結構比較穩定的情況下,即不會隨便添加子類。訪問者模式允許被訪問結構添加新的方法。
<?php
interface Visitor { // 抽象訪問者角色
public function visitConcreteElementA(ConcreteElementA $elementA);
public function visitConcreteElementB(concreteElementB $elementB);
}
interface Element { // 抽象節點角色
public function accept(Visitor $visitor);
}
class ConcreteVisitor1 implements Visitor { // 具體的訪問者1
public function visitConcreteElementA(ConcreteElementA $elementA) {}
public function visitConcreteElementB(ConcreteElementB $elementB) {}
}
class ConcreteVisitor2 implements Visitor { // 具體的訪問者2
public function visitConcreteElementA(ConcreteElementA $elementA) {}
public function visitConcreteElementB(ConcreteElementB $elementB) {}
}
class ConcreteElementA implements Element { // 具體元素A
private $_name;
public function __construct($name) { $this->_name = $name; }
public function getName() { return $this->_name; }
public function accept(Visitor $visitor) { // 接受訪問者調用它針對該元素的新方法
$visitor->visitConcreteElementA($this);
}
}
class ConcreteElementB implements Element { // 具體元素B
private $_name;
public function __construct($name) { $this->_name = $name;}
public function getName() { return $this->_name; }
public function accept(Visitor $visitor) { // 接受訪問者調用它針對該元素的新方法
$visitor->visitConcreteElementB($this);
}
}
class ObjectStructure { // 對象結構 即元素的集合
private $_collection;
public function __construct() { $this->_collection = array(); }
public function attach(Element $element) {
return array_push($this->_collection, $element);
}
public function detach(Element $element) {
$index = array_search($element, $this->_collection);
if ($index !== FALSE) {
unset($this->_collection[$index]);
}
return $index;
}
public function accept(Visitor $visitor) {
foreach ($this->_collection as $element) {
$element->accept($visitor);
}
}
}
// client
$elementA = new ConcreteElementA("ElementA");
$elementB = new ConcreteElementB("ElementB");
$elementA2 = new ConcreteElementB("ElementA2");
$visitor1 = new ConcreteVisitor1();
$visitor2 = new ConcreteVisitor2();
$os = new ObjectStructure();
$os->attach($elementA);
$os->attach($elementB);
$os->attach($elementA2);
$os->detach($elementA);
$os->accept($visitor1);
$os->accept($visitor2);
?>
22. Mediator(中介者模式)
中介者模式用於開發一個對象,這個對象能夠在類似對象相互之間不直接相互的情況下傳送或者調解對這些對象的集合的修改。 一般處理具有類似屬性,需要保持同步的非耦合對象時,最佳的做法就是中介者模式。PHP中不是特別常用的設計模式。
<?php
abstract class Mediator { // 中介者角色
abstract public function send($message,$colleague);
}
abstract class Colleague { // 抽象對象
private $_mediator = null;
public function __construct($mediator) {
$this->_mediator = $mediator;
}
public function send($message) {
$this->_mediator->send($message,$this);
}
abstract public function notify($message);
}
class ConcreteMediator extends Mediator { // 具體中介者角色
private $_colleague1 = null;
private $_colleague2 = null;
public function send($message,$colleague) {
//echo $colleague->notify($message);
if($colleague == $this->_colleague1) {
$this->_colleague1->notify($message);
} else {
$this->_colleague2->notify($message);
}
}
public function set($colleague1,$colleague2) {
$this->_colleague1 = $colleague1;
$this->_colleague2 = $colleague2;
}
}
class Colleague1 extends Colleague { // 具體對象角色
public function notify($message) {
echo 'colleague1:'.$message."<br>";
}
}
class Colleague2 extends Colleague { // 具體對象角色
public function notify($message) {
echo 'colleague2:'.$message."<br>";
}
}
// client
$objMediator = new ConcreteMediator();
$objC1 = new Colleague1($objMediator);
$objC2 = new Colleague2($objMediator);
$objMediator->set($objC1,$objC2);
$objC1->send("to c2 from c1"); //輸出:colleague1:to c2 from c1
$objC2->send("to c1 from c2"); //輸出:colleague2:to c1 from c2
?>
23. Interpreter(解釋器模式)
給定一個語言, 定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。
角色:
環境角色(PlayContent):定義解釋規則的全局信息。
抽象解釋器(Empress):定義了部分解釋具體實現,封裝了一些由具體解釋器實現的接口。
具體解釋器(MusicNote):實現抽象解釋器的接口,進行具體的解釋執行。
<?php
class Expression { //抽象表示
function interpreter($str) {
return $str;
}
}
class ExpressionNum extends Expression { //表示數字
function interpreter($str) {
switch($str) {
case "0": return "零";
case "1": return "一";
case "2": return "二";
case "3": return "三";
case "4": return "四";
case "5": return "五";
case "6": return "六";
case "7": return "七";
case "8": return "八";
case "9": return "九";
}
}
}
class ExpressionCharater extends Expression { //表示字符
function interpreter($str) {
return strtoupper($str);
}
}
class Interpreter { //解釋器
function execute($string) {
$expression = null;
for($i = 0;$i<strlen($string);$i++) {
$temp = $string[$i];
switch(true) {
case is_numeric($temp): $expression = new ExpressionNum(); break;
default: $expression = new ExpressionCharater();
}
echo $expression->interpreter($temp);
echo "<br>";
}
}
}
//client
$obj = new Interpreter();
$obj->execute("123s45abc");
/* 輸出:
一
二
三
S
四
五
A
B
C */
?>
詳細源碼Github地址:https://github.com/ityangs/PhpDesignPatterns.git