1. 程式人生 > >php的23中設計模式

php的23中設計模式

原文地址 https://my.oschina.net/botkenni/blog/1603660

 

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(享元模式)

運用共享技術有效的支援大量細粒度的物件

享元模式變化的是物件的儲存開銷

享元模式中主要角色: