天天看點

PhpDesignPatterns 「PHP 中的設計模式」

作者:微信APP爬蟲等開發

一、 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(建造者模式)

建造者模式主要在于建立一些複雜的對象。将一個複雜對象的構造與它的表示分離,使同樣的建構過程可以建立不同的表示的設計模式;

結構圖:

PhpDesignPatterns 「PHP 中的設計模式」
<?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
?>           

備忘錄模式又叫做快照模式(Snapshot)或 Token 模式,備忘錄模式的用意是在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态,這樣就可以在合适的時候将該對象恢複到原先儲存的狀态。

我們在程式設計的時候,經常需要儲存對象的中間狀态,當需要的時候,可以恢複到這個狀态。比如,我們使用Eclipse進行程式設計時,假如編寫失誤(例如不小心誤删除了幾行代碼),我們希望傳回删除前的狀态,便可以使用Ctrl+Z來進行傳回。這時我們便可以使用備忘錄模式來實作。

UML:

PhpDesignPatterns 「PHP 中的設計模式」

備忘錄模式所涉及的角色有三個:備忘錄(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類圖:

PhpDesignPatterns 「PHP 中的設計模式」

角色:

上下文環境(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中的數組本來就是一個可以放置任何類型資料的集合)

适用性

通路者模式多用在聚集類型多樣的情況下。在普通的形式下必須判斷每個元素是屬于什麼類型然後進行相應的操作,進而誕生出冗長的條件轉移語句。而通路者模式則可以比較好的解決這個問題。對每個元素統一調用element−>accept(vistor)即可。

通路者模式多用于被通路的類結構比較穩定的情況下,即不會随便添加子類。通路者模式允許被通路結構添加新的方法。

<?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 */
?>
            

繼續閱讀