設計模式(Design Patterns)
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 –百度百科
In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. –Wikipedia
在軟件開發過程中,一個功能的實現方式多種多樣,不同方法的可擴展性、可維護性以及複用性都是不一樣的。隨着一個人對自己項目代碼的要求增加,他會逐漸思考和實踐出自己的一套方法或者思想,這種方法或思想決定了他設計出的架構或者編寫出的代碼的質量優劣。設計模式就屬於這樣一種經驗的積累,是由大量優秀的工程師或者架構師總結和提煉的精華,學習好設計模式等於讓我們站在了巨人的肩膀上,從一個高的起點出發,可以避免走很多彎路。
設計模式的使用一定是根據場景來選擇的,而且設計模式的實現方式也不是固定的,我們一定要在理解現有設計模式的基礎上,根據自己實際的情況不斷實踐不斷理解。就像所謂的《泡妞大全》,讀千萬遍都不如實踐一次來的實際。
如果你對設計模式完全沒有感覺,那麼去好好寫一個類庫,或者一個簡單的MVC框架,這個過程會讓你感覺到自己缺失的部分。
設計模式的優點:
1、單一原則:一個類只負責功能領域中的相應職責(如 一個老師只管教育學生。)
單一職責原則是實現高內聚、低耦合的指導方針,它是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,而發現類的多重職責需要設計人員具有較強的分析設計能力和相關實踐經驗。
2、開閉原則:一個軟件實體應當對擴展開放,對修改關閉。(如 坐飛機,你只能乘坐,不能對他的飛行軌跡進行指控。但是如果你有需要,可以根據不同的線路來乘坐。)
3、里氏代換原則:所有引用基類(父類)的地方必須能透明地使用其子類的對象。(如: 我喜歡動物,那我一定喜歡狗,因爲狗是動物的子類;但是我喜歡狗,不能確定我喜歡其他動物。)
4、依賴倒置原則:抽象不應改依賴於細節,細節依賴與抽象。 (要針對接口編程,而不是針對實現編程。)
5、接口隔離原則:使用多個專門的接口,而不是統一的接口,把功能細分化。使客戶端在調用的時候不依賴他不需要的接口。
6、迪米特法則:一個軟件實體,儘量少的與其他實體相互的發生作用。
分類
在《設計模式:可複用面向對象軟件的基礎》(Design Patterns: Elements of Reusable Object-Oriented Software) 這本書中,作者把設計模式分了三大類:
創建型模式(Creational patterns)
創建型模式是爲了解決創建對象時候遇到的問題。因爲基本的對象創建方式可能會導致設計上的問題,或增加設計的複雜度。創建型模式有兩個主導思想:一是將系統使用的具體類封裝起來,二是隱藏這些具體類的實例創建和結合方式。
最常見的五種創建型模式如下:
- 工廠方法模式(Factory method pattern)
- 抽象工廠模式(Abstract factory pattern)
- 單例模式(Singleton pattern)
- 建造者模式(Builder pattern)
- 原型模式(Prototype pattern)
結構型模式(Structural pattern)
結構型模式是通過定義一個簡單的方法來實現和了解實體間關係,從而簡化設計。
- 適配器模式(Adapter pattern)
- 橋接模式(Bridge pattern)
- 合成模式(Composite pattern)
- 裝飾器模式(Decorator pattern)
- 門面模式(Facade pattern)
- 代理模式(Proxy pattern)
- 享元模式(Flyweight Pattern)
行爲型模式(Behavioral pattern)
行爲型模式用來識別對象之間的常用交流模式並加以實現,使得交流變得更加靈活。
- 策略模式(Strategy pattern)
- 模板方法模式(Template method pattern)
- 觀察者模式(Observer pattern)
- 迭代器模式(Iterator pattern)
- 責任鏈模式(Chain of responsibility pattern)
- 命令模式(Command pattern)
- 備忘錄模式(Memento pattern)
- 狀態模式(State pattern)
- 訪問者模式(Visitor pattern)
- 中介者模式(Mediator pattern)
- 解釋器模式(Interpreter pattern)
關係
這裏有一張各個模式關係圖,可以在瞭解各個模式以後梳理一下
參考
建造者模式(Builder pattern)
建造者模式是一種創建型模式,它可以讓一個產品的內部表象和和產品的生產過程分離開,從而可以生成具有不同內部表象的產品。
Builder模式中主要角色
- 抽象建造者(Builder)角色:定義抽象接口,規範產品各個部分的建造,必須包括建造方法和返回方法。
- 具體建造者(Concrete)角色:實現抽象建造者接口。應用程序最終根據此角色中實現的業務邏輯創造產品。
- 導演者(Director)角色:調用具體的建造者角色創造產品。
- 產品(Product)角色:在導演者的指導下所創建的複雜對象。
適用性
- 當創建複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
- 當構造過程必須允許被構造的對象有不同的表示時。
類圖
實例
<?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();
?>
優缺點
優點
建造者模式可以很好的將一個對象的實現與相關的“業務”邏輯分離開來,從而可以在不改變事件邏輯的前提下,使增加(或改變)實現變得非常容易。
缺點
建造者接口的修改會導致所有執行類的修改。
參考
單例模式(Singleton pattern)
抽象工廠模式是一種創建型模式,在應用這個模式時,單例對象的類必須保證只有一個實例存在。
實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不爲空就返回這個引用,如果類保持的引用爲空就創建該類的實例並將實例的引用賦予該類保持的引用;同時我們還將該類的構造函數定義爲私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。
單例模式中主要角色
Singleton定義一個getInstance操作,允許客戶訪問它唯一的實例。
這個例子也簡單,就像我有6個老婆(快醒醒!),她們在喊”老公”的時候都是指我。不管什麼時候,喊老公擦地,做飯,洗衣服都是指同一個人,PHP不編寫多線程,所以不存在搶佔問題,如果換別的語言編寫,一定得考慮到搶佔問題!老公是不可以邊擦地邊做飯的!
適用性
- 當類只能有一個實例而且客戶可以從一個衆所周知的訪問點訪問它時
- 當這個唯一實例應該是通過子類化可擴展的。並且用戶應該無需更改代碼就能使用一個擴展的實例時。
類圖
實例
<?php
public class Singleton {
private static $_instance = NULL;
// 私有構造方法
private function __construct() {}
public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new Singleton();
}
return self::$_instance;
}
// 防止克隆實例
public function __clone(){
die('Clone is not allowed.' . E_USER_ERROR);
}
}
?>
在此實例中,Singleton禁止了克隆及外部初始化,使得此類只可以通過getInstance()
方法來獲得實例,而這個實例只會在第一次使用時創建,以後每次都獲得同一實例。
優缺點
#### 優點
- 對唯一實例的受控訪問
- 縮小命名空間 單例模式是對全局變量的一種改進。它避免了那些存儲唯一實例的全局變量污染命名空間
- 允許對操作和表示的精華,單例類可以有子類。而且用這個擴展類的實例來配置一個應用是很容易的。你可以用你所需要的類的實例在運行時刻配置應用。
- 允許可變數目的實例(多例模式)
- 比類操作更靈活
缺點
單例模式在多線程的應用場合下必須小心使用。如果當唯一實例尚未創建時,有兩個線程同時調用創建方法,那麼它們同時沒有檢測到唯一實例的存在,從而同時各自創建了一個實例,這樣就有兩個實例被構造出來,從而違反了單例模式中實例唯一的原則。解決這個問題的辦法是爲指示類是否已經實例化的變量提供一個互斥鎖(雖然這樣會降低效率)。
參考
適配器模式(Adapter pattern)
適配器模式是一種結構型模式,它將一個類的接口轉接成用戶所期待的。一個適配使得因接口不兼容而不能在一起工作的類工作在一起,做法是將類別自己的接口包裹在一個已存在的類中。
適配器模式中主要角色
- 目標(Target)角色:定義客戶端使用的與特定領域相關的接口,這也就是我們所期待得到的
- 源(Adaptee)角色:需要進行適配的接口
- 適配器(Adapter)角色:對Adaptee的接口與Target接口進行適配;適配器是本模式的核心,適配器把源接口轉換成目標接口,此角色爲具體類
適用性
1、你想使用一個已經存在的類,而它的接口不符合你的需求
2、你想創建一個可以複用的類,該類可以與其他不相關的類或不可預見的類協同工作
3、你想使用一個已經存在的子類,但是不可能對每一個都進行子類化以匹配它們的接口。對象適配器可以適配它的父類接口(僅限於對象適配器)
類適配器模式與對象適配器
類適配器:Adapter與Adaptee是繼承關係
- 用一個具體的Adapter類和Target進行匹配。結果是當我們想要一個匹配一個類以及所有它的子類時,類Adapter將不能勝任工作
- 使得Adapter可以重定義Adaptee的部分行爲,因爲Adapter是Adaptee的一個子集
- 僅僅引入一個對象,並不需要額外的指針以間接取得adaptee
對象適配器:Adapter與Adaptee是委託關係
- 允許一個Adapter與多個Adaptee同時工作。Adapter也可以一次給所有的Adaptee添加功能
- 使用重定義Adaptee的行爲比較困難
類圖
類適配器
對象適配器
實例
類適配器
<?php
interface Target {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee { // 源角色
public function sampleMethod1() {}
}
class Adapter extends Adaptee implements Target { // 適配后角色
public function sampleMethod2() {}
}
// client
$adapter = new Adapter();
$adapter->sampleMethod1();
$adapter->sampleMethod2();
?>
對象適配器
<?php
interface Target {
public function sampleMethod1();
public function sampleMethod2();
}
class Adaptee {
public function sampleMethod1() {}
}
class Adapter implements Target {
private $_adaptee;
public function __construct(Adaptee $adaptee) {
$this->_adaptee = $adaptee;
}
public function sampleMethod1() { $this->_adaptee->sampleMethod1(); }
public function sampleMethod2() {}
}
$adaptee = new Adaptee();
$adapter = new Adapter($adaptee);
$adapter->sampleMethod1();
$adapter->sampleMethod2();
?>
參考
橋接模式(Bridge pattern)
橋接模式是一種結構型模式,它是軟件設計模式中最複雜的模式之一,它把事物對象和其具體行爲、具體特徵分離開來,使它們可以各自獨立的變化。事物對象僅是一個抽象的概念。如“圓形”、“三角形”歸於抽象的“形狀”之下,而“畫圓”、“畫三角”歸於實現行爲的“畫圖”類之下,然後由“形狀”調用“畫圖”。
主要角色
- 抽象化(Abstraction)角色:定義抽象類的接口並保存一個對實現化對象的引用。
- 修正抽象化(Refined Abstraction)角色:擴展抽象化角色,改變和修正父類對抽象化的定義。
- 實現化(Implementor)角色:定義實現類的接口,不給出具體的實現。此接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以完全不同。實現化角色應當只給出底層操作,而抽象化角色應當只給出基於底層操作的更高一層的操作。
- 具體實現化(Concrete Implementor)角色:實現實現化角色接口並定義它的具體實現。
適用性
- 如果一個系統需要在構件的抽象化和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的聯繫。
- 設計要求實現化角色的任何改變不應當影響客戶端,或者說實現化角色的改變對客戶端是完全透明的。
- 一個構件有多於一個的抽象化角色和實現化角色,並且系統需要它們之間進行動態的耦合。
- 雖然在系統中使用繼承是沒有問題的,但是由於抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
類圖
實例
<?php
abstract class Abstraction { // 抽象化角色,抽象化給出的定義,並保存一個對實現化對象的引用。
protected $imp; // 對實現化對象的引用
public function operation() {
$this->imp->operationImp();
}
}
class RefinedAbstraction extends Abstraction { // 修正抽象化角色, 擴展抽象化角色,改變和修正父類對抽象化的定義。
public function __construct(Implementor $imp) {
$this->imp = $imp;
}
public function operation() { $this->imp->operationImp(); }
}
abstract class Implementor { // 實現化角色, 給出實現化角色的接口,但不給出具體的實現。
abstract public function operationImp();
}
class ConcreteImplementorA extends Implementor { // 具體化角色A
public function operationImp() {}
}
class ConcreteImplementorB extends Implementor { // 具體化角色B
public function operationImp() {}
}
// client
$abstraction = new RefinedAbstraction(new ConcreteImplementorA());
$abstraction->operation();
$abstraction = new RefinedAbstraction(new ConcreteImplementorB());
$abstraction->operation();
?>
優點
- 分離接口及其實現部分, 將Abstraction與Implementor分享有助於降低對實現部分編譯時刻的依賴性, 接口與實現分享有助於分層,從而產生更好的結構化系統
- 提高可擴充性
- 實現細節對客戶透明。
參考
合成模式(Composite pattern)
合成模式是一種結構型模式,它將對象組合成樹形結構以表示”部分-整體”的層次結構。Composite使用戶對單個對象和組合對象的使用具有一致性。
Composite變化的是一個對象的結構和組成。
主要角色
- 抽象組件(Component)角色:抽象角色,給參加組合的對象規定一個接口。在適當的情況下,實現所有類共有接口的缺省行爲。聲明一個接口用於訪問和管理Component的子組件
- 樹葉組件(Leaf)角色:在組合中表示葉節點對象,葉節點沒有子節點。在組合中定義圖元對象的行爲。
- 樹枝組件(Composite)角色:存儲子部件。定義有子部件的那些部件的行爲。在Component接口中實現與子部件有關的操作。
- 客戶端(Client):通過Component接口操縱組合部件的對象
適用性
- 你想表示對象的部分-整體層次結構。
- 你希望用戶忽略組合對象和單個對象的不同,用戶將統一地使用組合結構中的所有對象。
類圖
安全式合成模式
透明式合成模式
實例
安全式合成模式
在Composite類裏面聲明所有的用來管理子類對象的方法。這樣的做法是安全的。因爲樹葉類型的對象根本就沒有管理子類的方法,因此,如果客戶端對樹葉類對象使用這些方法時,程序會在編譯時期出錯。編譯通不過,就不會出現運行時期錯誤。這樣的缺點是不夠透明,因爲樹葉類和合成類將具有不同的接口。
<?php
interface Component {
public function getComposite(); //返回自己的實例
public function operation();
}
class Composite implements Component { // 樹枝組件角色
private $_composites;
public function __construct() { $this->_composites = array(); }
public function getComposite() { return $this; }
public function operation() {
foreach ($this->_composites as $composite) {
$composite->operation();
}
}
public function add(Component $component) { //聚集管理方法 添加一個子對象
$this->_composites[] = $component;
}
public function remove(Component $component) { // 聚集管理方法 刪除一個子對象
foreach ($this->_composites as $key => $row) {
if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
}
return FALSE;
}
public function getChild() { // 聚集管理方法 返回所有的子對象
return $this->_composites;
}
}
class Leaf implements Component {
private $_name;
public function __construct($name) { $this->_name = $name; }
public function operation() {}
public function getComposite() {return null;}
}
// client
$leaf1 = new Leaf('first');
$leaf2 = new Leaf('second');
$composite = new Composite();
$composite->add($leaf1);
$composite->add($leaf2);
$composite->operation();
$composite->remove($leaf2);
$composite->operation();
?>
透明式合成模式
在Composite類裏面聲明所有的用來管理子類對象的方法。這樣做的是好處是所有的組件類都有相同的接口。在客戶端看來,樹葉類和合成類對象的區別起碼在接口層次上消失了,客戶端可以同等的對待所有的對象。這就是透明形式的合成模式,缺點就是不夠安全,因爲樹葉類對象和合成類對象在本質上是有區別的。樹葉類對象不可能有下一個層次的對象,因此調用其添加或刪除方法就沒有意義了,這在編譯期間是不會出錯的,而只會在運行時期纔會出錯。
<?php
interface Component { // 抽象組件角色
public function getComposite(); // 返回自己的實例
public function operation(); // 示例方法
public function add(Component $component); // 聚集管理方法,添加一個子對象
public function remove(Component $component); // 聚集管理方法 刪除一個子對象
public function getChild(); // 聚集管理方法 返回所有的子對象
}
class Composite implements Component { // 樹枝組件角色
private $_composites;
public function __construct() { $this->_composites = array(); }
public function getComposite() { return $this; }
public function operation() { // 示例方法,調用各個子對象的operation方法
foreach ($this->_composites as $composite) {
$composite->operation();
}
}
public function add(Component $component) { // 聚集管理方法 添加一個子對象
$this->_composites[] = $component;
}
public function remove(Component $component) { // 聚集管理方法 刪除一個子對象
foreach ($this->_composites as $key => $row) {
if ($component == $row) { unset($this->_composites[$key]); return TRUE; }
}
return FALSE;
}
public function getChild() { // 聚集管理方法 返回所有的子對象
return $this->_composites;
}
}
class Leaf implements Component {
private $_name;
public function __construct($name) {$this->_name = $name;}
public function operation() {}
public function getComposite() { return null; }
public function add(Component $component) { return FALSE; }
public function remove(Component $component) { return FALSE; }
public function getChild() { return null; }
}
// client
$leaf1 = new Leaf('first');
$leaf2 = new Leaf('second');
$composite = new Composite();
$composite->add($leaf1);
$composite->add($leaf2);
$composite->operation();
$composite->remove($leaf2);
$composite->operation();
?>
優缺點
#### 優點
- 簡化客戶代碼
- 使得更容易增加新類型的組件
缺點
- 使你的設計變得更加一般化,容易增加組件也會產生一些問題,那就是很難限制組合中的組件
參考
裝飾器模式(Decorator pattern)
裝飾器模式是一種結構型模式,它動態的給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更爲靈活【GOF95】
裝飾模式是以對客戶透明的方式動態地給一個對象附加上更多的職責。這也就是說,客戶端並不會覺得對象在裝飾前和裝飾後有什麼不同。裝飾模式可以在不使用創造更多子類的情況下,將對象的功能加以擴展。
主要角色
- 抽象構件(Component)角色:定義一個對象接口,以規範準備接收附加職責的對象,從而可以給這些對象動態地添加職責。
- 具體構件(Concrete Component)角色:定義一個將要接收附加職責的類。
- 裝飾(Decorator)角色:持有一個指向Component對象的指針,並定義一個與Component接口一致的接口。
- 具體裝飾(Concrete 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() {}
}
class ConcreteDecoratorB extends Decorator { // 具體裝飾類B
public function __construct(Component $component) {
parent::__construct($component);
}
public function operation() {
parent::operation();
$this->addedOperationB();
}
public function addedOperationB() {}
}
class ConcreteComponent implements Component{
public function operation() {}
}
// clients
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);
$decoratorA->operation();
$decoratorB->operation();
?>
優缺點
優點
- 比靜態繼承更靈活;
- 避免在層次結構高層的類有太多的特徵
缺點
- 使用裝飾模式會產生比使用繼承關係更多的對象。並且這些對象看上去都很想像,從而使得查錯變得困難。
參考
門面模式(Facade pattern)
門面模式是一種結構型模式,它爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層次的接口,使得子系統更加容易使用。
主要角色
門面(Facade)角色
- 此角色將被客戶端調用
- 知道哪些子系統負責處理請求
- 將用戶的請求指派給適當的子系統
子系統(subsystem)角色
- 實現子系統的功能
- 處理由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();
?>
優缺點
優點
- 它對客戶屏蔽了子系統組件,因而減少了客戶處理的對象的數目並使得子系統使用起來更加方便
- 實現了子系統與客戶之間的鬆耦合關係
- 如果應用需要,它並不限制它們使用子系統類。因此可以在系統易用性與能用性之間加以選擇
參考
享元模式(Flyweight Pattern)
享元模式是一種結構型模式,它使用共享物件,用來儘可能減少內存使用量以及分享資訊給儘可能多的相似物件;它適合用於當大量物件只是重複因而導致無法令人接受的使用大量內存。通常物件中的部分狀態是可以分享。常見做法是把它們放在外部數據結構,當需要使用時再將它們傳遞給享元。
主要角色
- 抽象享元(Flyweight角色:此角色是所有的具體享元類的超類,爲這些類規定出需要實現的公共接口。那些需要外蘊狀態的操作可以通過調用商業以參數形式傳入
- 具體享元(ConcreteFlyweight角色:實現Flyweight接口,併爲內部狀態(如果有的話)拉回存儲空間。ConcreteFlyweight對象必須是可共享的。它所存儲的狀態必須是內部的
- 不共享的具體享元(UnsharedConcreteFlyweight)角色:並非所有的Flyweight子類都需要被共享。Flyweigth使共享成爲可能,但它並不強制共享。
- 享元工廠(FlyweightFactory)角色:負責創建和管理享元角色。本角色必須保證享元對象可能被系統適當地共享
- 客戶端(Client)角色:本角色需要維護一個對所有享元對象的引用。本角色需要自行存儲所有享元對象的外部狀態
適用性
- 一個應用程序使用了大量的對象
- 完全由於使用大量的對象,造成很大的存儲開銷
- 對象的大多數狀態都可變爲外部狀態
- 如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象
- 應用程序不依賴於對象標識。
類圖
實例
<?php
abstract class Flyweight { // 抽象享元角色
abstract public function operation($state);
}
class ConcreteFlyweight extends Flyweight { // 具體享元角色
private $_intrinsicState = null;
public function __construct($state) {
$this->_intrinsicState = $state;
}
public function operation($state) {}
}
class UnsharedConcreteFlyweight extends Flyweight { // 不共享的具體享元,客戶端直接調用
private $_intrinsicState = null;
public function __construct($state) {
$this->_intrinsicState = $state;
}
public function operation($state) {}
}
class FlyweightFactory { // 享元工廠角色
private $_flyweights;
public function __construct() {
$this->_flyweights = array();
}
public function getFlyweigth($state) {
if (isset($this->_flyweights[$state])) {
return $this->_flyweights[$state];
} else {
return $this->_flyweights[$state] = new ConcreteFlyweight($state);
}
}
}
// client
$flyweightFactory = new FlyweightFactory();
$flyweight = $flyweightFactory->getFlyweigth('state A');
$flyweight->operation('other state A');
$flyweight = $flyweightFactory->getFlyweigth('state B');
$flyweight->operation('other state B');
// 不共享的對象,單獨調用
$uflyweight = new UnsharedConcreteFlyweight('state A');
$uflyweight->operation('other state A');
?>
優缺點
優點
- Flyweight模式可以大幅度地降低內存中對象的數量。
缺點
- Flyweight模式使得系統更加複雜
- Flyweigth模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長
參考
觀察者模式(Observer pattern)
觀察者模式是一種行爲型模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
又稱爲發佈-訂閱(Publish-Subscribe)模式、模型-視圖(Model-View)模式、源-監聽(Source-Listener)模式、或從屬者(Dependents)模式
主要角色
- 抽象主題(Subject)角色:主題角色將所有對觀察者對象的引用保存在一個集合中,每個主題可以有任意多個觀察者。抽象主題提供了增加和刪除觀察者對象的接口。
- 抽象觀察者(Observer)角色:爲所有的具體觀察者定義一個接口,在觀察的主題發生改變時更新自己。
- 具體主題(ConcreteSubject)角色:存儲相關狀態到具體觀察者對象,當具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體子類實現。
- 具體觀察者(ConcretedObserver)角色:存儲一個具體主題對象,存儲相關狀態,實現抽象觀察者角色所要求的更新接口,以使得其自身狀態和主題的狀態保持一致。
適用性
- 當一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。
- 當對一個對象的改變需要同時改變其它對象,而不知道具體有多少個對象待改變。
- 當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換句話說,你不希望這些對象是緊密耦合的。
類圖
實例
<?php
interface Subject { // 抽象主題角色
public function attach(Observer $observer); // 增加一個新的觀察者對象
public function detach(Observer $observer); // 刪除一個已註冊過的觀察者對象
public function notifyObservers(); // 通知所有註冊過的觀察者對象
}
class ConcreteSubject implements Subject { // 具體主題角色
private $_observers;
public function __construct() { $this->_observers = array(); }
public function attach(Observer $observer) {
return array_push($this->_observers, $observer);
}
public function detach(Observer $observer) {
$index = array_search($observer, $this->_observers);
if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
return FALSE;
}
unset($this->_observers[$index]);
return TRUE;
}
public function notifyObservers() {
if (!is_array($this->_observers)) { return FALSE; }
foreach ($this->_observers as $observer) {
$observer->update();
}
return TRUE;
}
}
interface Observer { // 抽象觀察者角色
public function update(); // 更新方法
}
class ConcreteObserver implements Observer {
private $_name;
public function __construct($name) { $this->_name = $name; }
public function update() {}
}
$subject = new ConcreteSubject();
/* 添加第一個觀察者 */
$observer1 = new ConcreteObserver('Mac');
$subject->attach($observer1);
$subject->notifyObservers(); // 主題變化,通知觀察者
/* 添加第二個觀察者 */
$observer2 = new ConcreteObserver('Win');
$subject->attach($observer2);
$subject->notifyObservers();
$subject->detach($observer1);
$subject->notifyObservers();
?>
優缺點
優點
- 觀察者和主題之間的耦合度較小。
- 支持廣播通信。
缺點
- 由於觀察者並不知道其它觀察者的存在,它可能對改變目標的最終代價一無所知。這可能會引起意外的更新。
參考
原型模式(Prototype pattern)
原型模式是一種創建者模式,其特點在於通過“複製”一個已經存在的實例來返回新的實例,而不是新建實例。
原型模式中主要角色
- 抽象原型(Prototype)角色:聲明一個克隆自己的接口
- 具體原型(Concrete Prototype)角色:實現一個克隆自己的操作
適用性
- 當一個系統應該獨立於它的產品創建、構成和表示時,要使用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 Demo {}
// client
$demo = new Demo();
$object1 = new ConcretePrototype($demo);
$object2 = $object1->copy();
?>
優缺點
優點
- 可以在運行時刻增加和刪除產品
- 可以改變值以指定新對象
- 可以改變結構以指定新對象
- 減少子類的構造
- 用類動態配置應用
缺點
Prototype模式的最主要缺點就是每一個類必須配備一個克隆方法。而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事。
參考
代理模式(Proxy pattern)
代理模式是一種結構型模式,它可以爲其他對象提供一種代理以控制對這個對象的訪問。
主要角色
- 抽象主題角色(Subject):它的作用是統一接口。此角色定義了真實主題角色和代理主題角色共用的接口,這樣就可以在使用真實主題角色的地方使用代理主題角色。
- 真實主題角色(RealSubject):隱藏在代理角色後面的真實對象。
- 代理主題角色(ProxySubject):它的作用是代理真實主題,在其內部保留了對真實主題角色的引用。它與真實主題角色都繼承自抽象主題角色,保持接口的統一。它可以控制對真實主題的存取,並可能負責創建和刪除真實對象。代理角色並不是簡單的轉發,通常在將調用傳遞給真實對象之前或之後執行某些操作,當然你也可以只是簡單的轉發。 與適配器模式相比:適配器模式是爲了改變對象的接口,而代理模式並不能改變所代理對象的接口。
適用性
- 爲一些複雜的子系統提供一組接口
- 提高子系統的獨立性
- 在層次化結構中,可以使用門面模式定義系統的每一層的接口
類圖
實例
<?php
abstract class Subject { // 抽象主題角色
abstract public function action();
}
class RealSubject extends Subject { // 真實主題角色
public function __construct() {}
public function action() {}
}
class ProxySubject extends Subject { // 代理主題角色
private $_real_subject = NULL;
public function __construct() {}
public function action() {
$this->_beforeAction();
if (is_null($this->_real_subject)) {
$this->_real_subject = new RealSubject();
}
$this->_real_subject->action();
$this->_afterAction();
}
private function _beforeAction() {}
private function _afterAction() {}
}
// client
$subject = new ProxySubject();
$subject->action();
?>
參考
策略模式(Strategy pattern)
策略模式是一種行爲型模式,它定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。策略模式可以使算法可獨立於使用它的客戶而變化。
主要角色
- 抽象策略(Strategy)角色:定義所有支持的算法的公共接口。通常是以一個接口或抽象來實現。Context使用這個接口來調用其ConcreteStrategy定義的算法
- 具體策略(ConcreteStrategy)角色:以Strategy接口實現某具體算法
- 環境(Context)角色:持有一個Strategy類的引用,用一個ConcreteStrategy對象來配置
適用性
- 許多相關的類僅僅是行爲有異。“策略”提供了一種用多個行爲中的一個行爲來配置一個類的方法
- 需要使用一個算法的不同變體。
- 算法使用客戶不應該知道的數據。可使用策略模式以避免暴露覆雜的,與算法相關的數據結構
- 一個類定義了多種行爲,並且 這些行爲在這個類的操作中以多個形式出現。將相關的條件分支移和它們各自的Strategy類中以代替這些條件語句
類圖
實例
<?php
interface Strategy { // 抽象策略角色,以接口實現
public function algorithmInterface(); // 算法接口
}
class ConcreteStrategyA implements Strategy { // 具體策略角色A
public function algorithmInterface() {}
}
class ConcreteStrategyB implements Strategy { // 具體策略角色B
public function algorithmInterface() {}
}
class ConcreteStrategyC implements Strategy { // 具體策略角色C
public function algorithmInterface() {}
}
class Context { // 環境角色
private $_strategy;
public function __construct(Strategy $strategy) {
$this->_strategy = $strategy;
}
public function contextInterface() {
$this->_strategy->algorithmInterface();
}
}
// client
$strategyA = new ConcreteStrategyA();
$context = new Context($strategyA);
$context->contextInterface();
$strategyB = new ConcreteStrategyB();
$context = new Context($strategyB);
$context->contextInterface();
$strategyC = new ConcreteStrategyC();
$context = new Context($strategyC);
$context->contextInterface();
?>
優缺點
優點
- 策略模式提供了管理相關的算法族的辦法
- 策略模式提供了可以替換繼承關係的辦法 將算封閉在獨立的Strategy類中使得你可以獨立於其Context改變它
- 使用策略模式可以避免使用多重條件轉移語句。
缺點
- 客戶必須瞭解所有的策略 這是策略模式一個潛在的缺點
- Strategy和Context之間的通信開銷
- 策略模式會造成很多的策略類
參考
命令模式(Command pattern)
命令模式是一種行爲型模式,它將一個請求封裝爲一個對象,從而使用你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤消的操作。命令模式把發出命令的責任和執行命令的責任分割開,委派給不同的對象。
請求的一方發出請求要求執行一個操作;接收的一方收到請求,並執行操作。命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。
主要角色
- 命令(Command)角色:聲明瞭一個給所有具體命令類的抽象接口。這是一個抽象角色。
- 具體命令(ConcreteCommand)角色:定義一個接受者和行爲之間的弱耦合;實現Execute()方法,負責調用接收考的相應操作。Execute()方法通常叫做執行方法。
- 客戶(Client)角色:創建了一個具體命令(ConcreteCommand)對象並確定其接收者。
- 請求者(Invoker)角色:負責調用命令對象執行請求,相關的方法叫做行動方法。
- 接收者(Receiver)角色:負責具體實施和執行一個請求。任何一個類都可以成爲接收者,實施和執行請求的方法叫做行動方法。
適用性
- 抽象出待執行的動作以參數化對象。Command模式是回調機制的一個面向對象的替代品。
- 在不同的時刻指定、排列和執行請求。
- 支持取消操作。
- 支持修改日誌。
- 用構建在原語操作上的高層操作構造一個系統。Command模式提供了對事務進行建模的方法。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() { }
}
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();
?>
優缺點
優點
- 命令模式把請求一個操作的對象與知道怎麼執行一個操作的對象分離開。
- 命令類與其他任何別的類一樣,可以修改和推廣。
- 可以把命令對象聚合在一起,合成爲合成命令。
- 可以很容易的加入新的命令類。
缺點
- 可能會導致某些系統有過多的具體命令類。
參考
解釋器模式(Interpreter pattern)
解釋器模式是一種行爲型模式,它給定一個語言, 定義它的文法的一種表示,並定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子。
類圖
實例
<?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);
}
}
}
//client
$obj = new Interpreter();
$obj->execute("12345abc");
?>
參考
迭代器模式(Iterator pattern)
迭代器模式是一種行爲型模式,它是一種最簡單也最常見的設計模式。它可以讓使用者透過特定的接口巡訪容器中的每一個元素而不用瞭解底層的實作。
適用性
- 在希望利用語言本身的遍歷函數便利自定義結構時,例如PHP中的foreach函數
實例
<?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 />';
}
?>
參考
中介者模式(Mediator pattern)
中介者模式是一種行爲型模式,它包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯作用,從而使它們可以鬆散偶合。當某些對象之間的作用發生改變時,不會立即影響其他的一些對象之間的作用,保證這些作用可以彼此獨立的變化。
主要角色
- 中介者(Mediator)角色:定義了對象間相互作用的接口
- 具體中介者(ConcreteMediator)角色:實現了中介者定義的接口。
- 具體對象(ConcreteColleague)角色:通過中介者和別的對象進行交互
實例
<?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) {
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) { }
}
class Colleague2 extends Colleague { // 具體對象角色
public function notify($message) { }
}
// client
$objMediator = new ConcreteMediator();
$objC1 = new Colleague1($objMediator);
$objC2 = new Colleague2($objMediator);
$objMediator->set($objC1,$objC2);
$objC1->send("to c2 from c1");
$objC2->send("to c1 from c2");
?>
參考
備忘錄模式(Memento pattern)
備忘錄模式是一種行爲型模式,它在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣可以在以後把該對象的狀態恢復到之前保存的狀態。
主要角色
- 備忘錄(Memento)角色:存儲發起人(Originator)對象的內部狀態,而發起人根據需要決定備忘錄存儲發起人的哪些內部狀態。備忘錄可以保護其內容不被發起人(Originator)對象之外的任何對象所讀取。
- 發起人(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() {}
}
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($caretaker->getMemento());
$org->showState();
?>
優缺點
優點
- 有時一些發起人對象的內部信息必須保存在發起人對象以外的地方,但是必須要由發起人對象自己讀取。
- 簡化了發起人(Originator)類。發起人(Originator)不再需要管理和保存其內部狀態的一個個版本,客戶端可以自行管理它們所需要的這些狀態的版本
- 當發起人角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態復原。
缺點
- 如果發起人角色的狀態需要完整地存儲到備忘錄對象中,那麼在資源消耗上面備忘錄對象會很昂貴。
- 當負責人角色將一個備忘錄存儲起來的時候,負責人可能並不知道這個狀態會佔用多大的存儲空間,從而無法提醒用戶一個操作是否會很昂貴。
- 當發起人角色的狀態改變的時候,有可能這個狀態無效。
參考
訪問者模式(Visitor pattern)
訪問者模式是一種行爲型模式,訪問者表示一個作用於某對象結構中各元素的操作。它可以在不修改各元素類的前提下定義作用於這些元素的新操作,即動態的增加具體訪問者角色。
訪問者模式利用了雙重分派。先將訪問者傳入元素對象的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);
?>
優缺點
優點
- 訪問者模式使得增加新的操作變得很容易。使用訪問者模式可以在不用修改具體元素類的情況下增加新的操作。它主要是通過元素類的accept方法來接受一個新的visitor對象來實現的。如果一些操作依賴於一個複雜的結構對象的話,那麼一般而言,增加新的操作會很複雜。而使用訪問者模式,增加新的操作就意味着增加一個新的訪問者類,因此,變得很容易。
- 訪問者模式將有關的行爲集中到一個訪問者對象中,而不是分散到一個個的節點類中。
- 訪問者模式可以跨過幾個類的等級結構訪問屬於不同的等級結構的成員類。迭代子只能訪問屬於同一個類型等級結構的成員對象,而不能訪問屬於不同等級結構的對象。訪問者模式可以做到這一點。
- 積累狀態。每一個單獨的訪問者對象都集中了相關的行爲,從而也就可以在訪問的過程中將執行操作的狀態積累在自己內部,而不是分散到很多的節點對象中。這是有益於系統維護的優點。
缺點
- 增加新的節點類變得很困難。每增加一個新的節點都意味着要在抽象訪問者角色中增加一個新的抽象操作,並在每一個具體訪問者類中增加相應的具體操作。
- 破壞封裝。訪問者模式要求訪問者對象訪問並調用每一個節點對象的操作,這隱含了一個對所有節點對象的要求:它們必須暴露一些自己的操作和內部狀態。不然,訪問者的訪問就變得沒有意義。由於訪問者對象自己會積累訪問操作所需的狀態,從而使這些狀態不再存儲在節點對象中,這也是破壞封裝的。
參考
策略模式(Strategy pattern)
狀態模式是一種行爲型模式,它允許一個對象在其內部狀態改變時改變它的行爲。對象看起來似乎修改了它的類,狀態模式變化的位置在於對象的狀態。
主要角色
- 抽象狀態(State)角色:定義一個接口,用以封裝環境對象的一個特定的狀態所對應的行爲
- 具體狀態(ConcreteState)角色:每一個具體狀態類都實現了環境(Context)的一個狀態所對應的行爲
- 環境(Context)角色:定義客戶端所感興趣的接口,並且保留一個具體狀態類的實例。這個具體狀態類的實例給出此環境對象的現有狀態
適用性
- 一個對象的行爲取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行爲
- 一個操作中含有龐大的多分支的條件語句,且這些分支依賴於該對象的狀態。這個狀態通常用一個或多個枚舉常量表示。通常,有多個操作包含這一相同的條件結構。State模式模式將每一個條件分支放入一個獨立的類中。這使得你可以要所對象自身的情況將對象的狀態作爲一個對象,這一對象可以不依賴於其他對象而獨立變化
類圖
實例
<?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) {
$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) {
$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();
?>
優缺點
優點
- 它將與特定狀態相關的行爲局部化
- 它使得狀態轉換顯示化
- State對象可被共享
參考
抽象工廠模式(Abstract Factory pattern)
抽象工廠模式是一種創建型模式,它提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。它的實質是“提供接口,創建一系列相關或獨立的對象,而不指定這些對象的具體類”。
抽象工廠模式提供一個創建一系統相關或相互依賴對象的接口,而無需指定它們具體的類。
抽象工廠模式中主要角色
- 抽象工廠(Abstract Factory)角色:它聲明創建抽象產品對象的接口
- 具體工廠(Concrete Factory)角色:實現創建產品對象的操作
- 抽象產品(Abstract Product)角色:聲明一類產品的接口
- 具體產品(Concrete Product)角色:實現抽象產品角色所定義的接口
這個和工廠方法模式類似,我們不再只要一個漢堡,可能是4個漢堡2個雞翅,我們還是對服務員說,服務員屬於具體工廠,抽象產品就是麥當勞可賣的食物,具體產品是我們跟服務員要的食物。
適用性
- 一個系統要獨立於它的產品的創建、組合和表示時。
- 一個系統要由多個產品系列中的一個來配置時。
- 需要強調一系列相關的產品對象的設計以便進行聯合使用時。
- 提供一個產品類庫,而只想顯示它們的接口而不是實現時。
類圖
實例
<?php
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(); }
}
?>
在這裏例子中,工廠類實現了一組工廠方法。如果要增加新的功能,可以增加新的接口,讓新的工廠類實現這個接口即可,而無需修改現有的工廠類。
優缺點
優點
- 分離了具體的類
- 使增加或替換產品族變得容易
- 有利於產品的一致性
缺點
難以支持新種類的產品。這是因爲AbstractFactory接口確定了可以被創建的產品集合。支持新各類的產品就需要擴展訪工廠接口,從而導致AbstractFactory類及其所有子類的改變。
參考
工廠方法模式(Factory method pattern)
工廠方法模式是一種創建型模式,這種模式使用“工廠”概念來完成對象的創建而不用具體說明這個對象。
在面向對象程序設計中,工廠通常是一個用來創建其他對象的對象。工廠是構造方法的抽象,用來實現不同的分配方案。
主要角色
- 抽象產品(Product)角色:具體產品對象共有的父類或者接口。
- 具體產品(Concrete Product)角色:實現抽象產品角色所定義的接口
- 抽象工廠(Creator)角色:它聲明瞭工廠方法,該方法返回Product對象
- 具體工廠(Concrete Creator):實現抽象工廠接口
工廠方法模式就像我們去麥當勞買漢堡,我們只要找到服務員,讓他幫我們拿來漢堡即可。其中具體某個服務員就像具體工廠,他繼承了服務員應有的服務。漢堡在到手以前屬於抽象產品,而我們拿到的漢堡就屬於具體產品。
適用性
- 創建對象需要大量重複的代碼(例如創建一個MySQL操作類,需要配置很多選項,這些都可以在工廠方法中進行)。
- 創建對象需要訪問某些信息,而這些信息不應該包含在複合類中。
- 創建對象的生命週期必須集中管理,以保證在整個程序中具有一致的行爲。
類圖
實例
普通工廠方法
下面的例子是工廠方法模式的應用,我們要創建兩種風格的按鈕,只需用不同的工廠方法獲得相應按鈕類即可。
<?php
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();
}
}
}
?>
上例中的createButton()
方法即所謂的工廠方法,它所在的類僅僅是這個方法的載體。工廠方法的核心功能是創建類並返回,這個方法可以產生一個類,也可以產生多種類。這個方法本身的載體也並不侷限,將其設置爲靜態方法也是可以的,這個根據自己的情況而定。
優缺點
優點
工廠方法模式可以允許系統在不修改工廠角色的情況下引進新產品。
缺點
- 重構已經存在的類會破壞客戶端代碼。
- 如果工廠方法所在類的構造函數爲私有,則工廠方法無法繼續擴展,或者必須實現工廠方法所在類的全部依賴方法。
參考
模板方法模式(Template method pattern)
模板方法模式模式是一種行爲型模式,它定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method 使得子類可以在不改變一個算法的結構的情況下重定義該算法的某些特定的步驟。
主要角色
抽象模板(AbstractClass)角色
定義一個或多個抽象方法讓子類實現。這些抽象方法叫做基本操作,它們是頂級邏輯的組成部分。
定義一個模板方法。這個模板方法一般是一個具體方法,它給出頂級邏輯的骨架,而邏輯的組成步驟在對應的抽象操作中,這些操作將會推遲到子類中實現。同時,頂層邏輯也可以調用具體的實現方法
具體模板(ConcrteClass)角色
實現父類的一個或多個抽象方法,作爲頂層邏輯的組成而存在。
每個抽象模板可以有多個具體模板與之對應,而每個具體模板有其自己對抽象方法(也就是頂層邏輯的組成部分)的實現,從而使得頂層邏輯的實現各不相同。
適用性
- 一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
- 各子類中公共的行爲應被提取出來並集中到一個公共父類中以避免代碼重複。
- 控制子類擴展。
類圖
實例
<?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();
?>
參考
責任鏈模式(Chain of responsibility pattern)
責任鏈模式是一種行爲型模式,它包含了一些命令對象和一系列的處理對象。每一個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。該模式還描述了往該處理鏈的末尾添加新的處理對象的方法。
主要角色
- 抽象責任(Responsibility)角色:定義所有責任支持的公共方法。
- 具體責任(Concrete Responsibility)角色:以抽象責任接口實現的具體責任
- 責任鏈(Chain of responsibility)角色:設定責任的調用規則
實例
<?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();
}
};
}
class ResponsibilityB extends Responsibility {
public function __construct() {}
public function operate(){
if (false == is_null($this->next)) {
$this->next->operate();
}
};
}
$res_a = new ResponsibilityA();
$res_b = new ResponsibilityB();
$res_a->setNext($res_b);
?>