超全的設計模式簡介(45種)

因爲掘金字數文章限制在 20000 之內,刪減了一部分,能夠前往 github 查看全文 超全的設計模式簡介(45種),該文建議配合 design-patterns-for-humans 中文版 一塊兒看。php

推薦閱讀

設計模式(Design pattern)表明了最佳的實踐,一般被有經驗的面向對象的軟件開發人員所採用。設計模式是軟件開發人員在軟件開發過程當中面臨的通常問題的解決方案。這些解決方案是衆多軟件開發人員通過至關長的一段時間的試驗和錯誤總結出來的。
設計模式是一套被反覆使用的、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。項目中合理地運用設計模式能夠完美地解決不少問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在咱們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是設計模式能被普遍應用的緣由。html

設計模式的類型

共有 23 種設計模式。這些模式能夠分爲三大類:前端

下面用一個圖片來總體描述一下設計模式之間的關係:java

設計模式的六大原則

一、開閉原則(Open Close Principle)

開閉原則的意思是:對擴展開放,對修改關閉。在程序須要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,咱們須要使用接口和抽象類,後面的具體設計中咱們會提到這點。ios

二、里氏代換原則(Liskov Substitution Principle)

里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。LSP 是繼承複用的基石,只有當派生類能夠替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被複用,而派生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。laravel

三、依賴倒轉原則(Dependence Inversion Principle)

這個原則是開閉原則的基礎,具體內容:針對接口編程,依賴於抽象而不依賴於具體。git

四、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另一個意思是:下降類之間的耦合度。因而可知,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調下降依賴,下降耦合。程序員

五、迪米特法則,又稱最少知道原則(Demeter Principle)

最少知道原則是指:一個實體應當儘可能少地與其餘實體之間發生相互做用,使得系統功能模塊相對獨立。github

六、合成複用原則(Composite Reuse Principle)

合成複用原則是指:儘可能使用合成 / 聚合的方式,而不是使用繼承。web

工廠模式

工廠模式(Factory Pattern)最經常使用的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
在工廠模式中,咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用一個共同的接口來指向新建立的對象。

介紹

意圖: 定義一個建立對象的接口,讓其子類本身決定實例化哪個工廠類,工廠模式使其建立過程延遲到子類進行。

主要解決: 主要解決接口選擇的問題。

什麼時候使用: 咱們明確地計劃不一樣條件下建立不一樣實例時。

如何解決: 讓其子類實現工廠接口,返回的也是一個抽象的產品。

關鍵代碼: 建立過程在其子類執行。

應用實例:

  1. 您須要一輛汽車,能夠直接從工廠裏面提貨,而不用去管這輛汽車是怎麼作出來的,以及這個汽車裏面的具體實現。
  2. Hibernate 換數據庫只需換方言和驅動就能夠。

優勢:

  1. 一個調用者想建立一個對象,只要知道其名稱就能夠了。
  2. 擴展性高,若是想增長一個產品,只要擴展一個工廠類就能夠。
  3. 屏蔽產品的具體實現,調用者只關心產品的接口。

缺點: 每次增長一個產品時,都須要增長一個具體類和對象實現工廠,使得系統中類的個數成倍增長,在必定程度上增長了系統的複雜度,同時也增長了系統具體類的依賴。這並非什麼好事。

使用場景:

  1. 日誌記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶能夠選擇記錄日誌到什麼地方。
  2. 數據庫訪問,當用戶不知道最後系統採用哪一類數據庫,以及數據庫可能有變化時。
  3. 設計一個鏈接服務器的框架,須要三個協議,"POP3"、"IMAP"、"HTTP",能夠把這三個做爲產品類,共同實現一個接口。

注意事項: 做爲一種建立類模式,在任何須要生成複雜對象的地方,均可以使用工廠方法模式。有一點須要注意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是只須要經過 new 就能夠完成建立的對象,無需使用工廠模式。若是使用工廠模式,就須要引入一個工廠類,會增長系統的複雜度。

抽象工廠模式

抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠建立其餘工廠。該超級工廠又稱爲其餘工廠的工廠。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
在抽象工廠模式中,接口是負責建立一個相關對象的工廠,不須要顯式指定它們的類。每一個生成的工廠都能按照工廠模式提供對象。

介紹

意圖: 提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

主要解決: 主要解決接口選擇的問題。

什麼時候使用: 系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。

如何解決: 在一個產品族裏面,定義多個產品。

關鍵代碼: 在一個工廠裏聚合多個同類產品。

應用實例: 工做了,爲了參加一些聚會,確定有兩套或多套衣服吧,好比說有商務裝(成套,一系列具體產品)、時尚裝(成套,一系列具體產品),甚至對於一個家庭來講,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產品。假設一種狀況(現實中是不存在的,要否則,無法進入共產主義了,但有利於說明抽象工廠模式),在您的家中,某一個衣櫃(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也天然要從這個衣櫃中取出了。用 OO 的思想去理解,全部的衣櫃(具體工廠)都是衣櫃類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另外一個抽象產品)。

優勢: 當一個產品族中的多個對象被設計成一塊兒工做時,它能保證客戶端始終只使用同一個產品族中的對象。

缺點: 產品族擴展很是困難,要增長一個系列的某一產品,既要在抽象的 Creator 里加代碼,又要在具體的裏面加代碼。

使用場景:

  1. QQ 換皮膚,一整套一塊兒換。
  2. 生成不一樣操做系統的程序。

注意事項: 產品族難擴展,產品等級易擴展。

單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。

注意:

  • 一、單例類只能有一個實例。
  • 二、單例類必須本身建立本身的惟一實例。
  • 三、單例類必須給全部其餘對象提供這一實例。

介紹

意圖: 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

主要解決: 一個全局使用的類頻繁地建立與銷燬。

什麼時候使用: 當您想控制實例數目,節省系統資源的時候。

如何解決: 判斷系統是否已經有這個單例,若是有則返回,若是沒有則建立。

關鍵代碼: 構造函數是私有的。

應用實例:

  1. 一個班級只有一個班主任。
  2. Windows 是多進程多線程的,在操做一個文件的時候,就不可避免地出現多個進程或線程同時操做一個文件的現象,因此全部文件的處理必須經過惟一的實例來進行。
  3. 一些設備管理器經常設計爲單例模式,好比一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。

優勢:

  1. 在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例(好比管理學院首頁頁面緩存)。
  2. 避免對資源的多重佔用(好比寫文件操做)。

缺點: 沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

使用場景:

  1. 要求生產惟一序列號。
  2. WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  3. 建立的一個對象須要消耗的資源過多,好比 I/O 與數據庫的鏈接等。

**注意事項:**getInstance() 方法中須要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入形成 instance 被屢次實例化。

建造者模式

建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立於其餘對象的。

介紹

意圖: 將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。

主要解決: 主要解決在軟件系統中,有時候面臨着 "一個複雜對象" 的建立工做,其一般由各個部分的子對象用必定的算法構成;因爲需求的變化,這個複雜對象的各個部分常常面臨着劇烈的變化,可是將它們組合在一塊兒的算法卻相對穩定。

什麼時候使用: 一些基本部件不會變,而其組合常常變化的時候。

如何解決: 將變與不變分離開。

關鍵代碼: 建造者:建立和提供實例,導演:管理建造出來的實例的依賴關係。

應用實例:

  1. 去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是常常變化的,生成出所謂的 "套餐"。
  2. JAVA 中的 StringBuilder。

優勢:

  1. 建造者獨立,易擴展。
  2. 便於控制細節風險。

缺點:

  1. 產品必須有共同點,範圍有限制。
  2. 如內部變化複雜,會有不少的建造類。

使用場景:

  1. 須要生成的對象具備複雜的內部結構。
  2. 須要生成的對象內部屬性自己相互依賴。

注意事項: 與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。

原型模式

原型模式(Prototype Pattern)是用於建立重複的對象,同時又能保證性能。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式是實現了一個原型接口,該接口用於建立當前對象的克隆。當直接建立對象的代價比較大時,則採用這種模式。例如,一個對象須要在一個高代價的數據庫操做以後被建立。咱們能夠緩存該對象,在下一個請求時返回它的克隆,在須要的時候更新數據庫,以此來減小數據庫調用。

介紹

意圖: 用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。

主要解決: 在運行期創建和刪除原型。

什麼時候使用:

  1. 當一個系統應該獨立於它的產品建立,構成和表示時。
  2. 當要實例化的類是在運行時刻指定時,例如,經過動態裝載。
  3. 爲了不建立一個與產品類層次平行的工廠類層次時。
  4. 當一個類的實例只能有幾個不一樣狀態組合中的一種時。創建相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。

如何解決: 利用已有的一個原型對象,快速地生成和原型對象同樣的實例。

關鍵代碼:

  1. 實現克隆操做,在 JAVA 繼承 Cloneable,重寫 clone(),在 .NET 中可使用 Object 類的 MemberwiseClone() 方法來實現對象的淺拷貝或經過序列化的方式來實現深拷貝。
  2. 原型模式一樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它一樣要求這些 "易變類" 擁有穩定的接口。

應用實例:

  1. 細胞分裂。
  2. JAVA 中的 Object clone() 方法。

優勢:

  1. 性能提升。
  2. 逃避構造函數的約束。

缺點:

  1. 配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。
  2. 必須實現 Cloneable 接口。

使用場景:

  1. 資源優化場景。
  2. 類初始化須要消化很是多的資源,這個資源包括數據. 硬件資源等。
  3. 性能和安全要求的場景。
  4. 經過 new 產生一個對象須要很是繁瑣的數據準備或訪問權限,則可使用原型模式。
  5. 一個對象多個修改者的場景。
  6. 一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值時,能夠考慮使用原型模式拷貝多個對象供調用者使用。
  7. 在實際項目中,原型模式不多單獨出現,通常是和工廠方法模式一塊兒出現,經過 clone 的方法建立一個對象,而後由工廠方法提供給調用者。原型模式已經與 Java 融爲渾然一體,你們能夠隨手拿來使用。

注意事項: 與經過對一個類進行實例化來構造新對象不一樣的是,原型模式是經過拷貝一個現有對象生成新對象的。淺拷貝實現 Cloneable,重寫,深拷貝是經過實現 Serializable 讀取二進制流。

對象池模式

對象池(也稱爲資源池)被用來管理對象緩存。對象池是一組已經初始化過且能夠直接使用的對象集合,用戶在使用對象時能夠從對象池中獲取對象,對其進行操做處理,並在不須要時歸還給對象池而非銷燬它。
若對象初始化、實例化的代價高,且須要常常實例化,但每次實例化的數量較少的狀況下,使用對象池能夠得到顯著的性能提高。常見的使用對象池模式的技術包括線程池、數據庫鏈接池、任務隊列池、圖片資源對象池等。
固然,若是要實例化的對象較小,不須要多少資源開銷,就沒有必要使用對象池模式了,這非但不會提高性能,反而浪費內存空間,甚至下降性能。

示例代碼

Pool.php

<?php

namespace DesignPatterns\Creational\Pool;

class Pool {

    private $instances = array();
    private $class;

    public function __construct($class) {
        $this->class = $class;
    }

    public function get() {
        if (count($this->instances) > 0) {
            return array_pop($this->instances);
        }

        return new $this->class();
    }

    public function dispose($instance) {
        $this->instances[] = $instance;
    }
}
複製代碼

Processor.php

<?php

namespace DesignPatterns\Creational\Pool;

class Processor {

    private $pool;
    private $processing = 0;
    private $maxProcesses = 3;
    private $waitingQueue = [];

    public function __construct(Pool $pool) {
        $this->pool = $pool;
    }

    public function process($image) {
        if ($this->processing++ < $this->maxProcesses) {
            $this->createWorker($image);
        } else {
            $this->pushToWaitingQueue($image);
        }
    }

    private function createWorker($image) {
        $worker = $this->pool->get();
        $worker->run($image, array($this, 'processDone'));
    }

    public function processDone($worker) {
        $this->processing--;
        $this->pool->dispose($worker);

        if (count($this->waitingQueue) > 0) {
            $this->createWorker($this->popFromWaitingQueue());
        }
    }

    private function pushToWaitingQueue($image) {
        $this->waitingQueue[] = $image;
    }

    private function popFromWaitingQueue() {
        return array_pop($this->waitingQueue);
    }
}
複製代碼

Worker.php

<?php

namespace DesignPatterns\Creational\Pool;

class Worker {

    public function __construct() {
        // let's say that constuctor does really expensive work...
        // for example creates "thread"
    }

    public function run($image, array $callback) {
        // do something with $image...
        // and when it's done, execute callback
        call_user_func($callback, $this);
    }
}
複製代碼

多例模式

多例模式和單例模式相似,但能夠返回多個實例。好比咱們有多個數據庫鏈接,MySQL、SQLite、Postgres,又或者咱們有多個日誌記錄器,分別用於記錄調試信息和錯誤信息,這些均可以使用多例模式實現。

示例代碼

Multiton.php

<?php

namespace DesignPatterns\Creational\Multiton;

/** * Multiton類 */
class Multiton {
    /** * * 第一個實例 */
    const INSTANCE_1 = '1';

    /** * * 第二個實例 */
    const INSTANCE_2 = '2';

    /** * 實例數組 * * @var array */
    private static $instances = array();

    /** * 構造函數是私有的,不能從外部進行實例化 * */
    private function __construct() {
    }

    /** * 經過指定名稱返回實例(使用到該實例的時候纔會實例化) * * @param string $instanceName * * @return Multiton */
    public static function getInstance($instanceName) {
        if (!array_key_exists($instanceName, self::$instances)) {
            self::$instances[$instanceName] = new self();
        }

        return self::$instances[$instanceName];
    }

    /** * 防止實例從外部被克隆 * * @return void */
    private function __clone() {
    }

    /** * 防止實例從外部反序列化 * * @return void */
    private function __wakeup() {
    }
}
複製代碼

靜態工廠模式

與簡單工廠相似,該模式用於建立一組相關或依賴的對象,不一樣之處在於靜態工廠模式使用一個靜態方法來建立全部類型的對象,該靜態方法一般是 factory 或 build。

示例代碼

StaticFactory.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

class StaticFactory {
    /** * 經過傳入參數建立相應對象實例 * * @param string $type * * @static * * @throws \InvalidArgumentException * @return FormatterInterface */
    public static function factory($type) {
        $className = __NAMESPACE__ . '\Format' . ucfirst($type);

        if (!class_exists($className)) {
            throw new \InvalidArgumentException('Missing format class.');
        }

        return new $className();
    }
}
複製代碼

FormatterInterface.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

/** * FormatterInterface接口 */
interface FormatterInterface {
}
複製代碼

FormatString.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

/** * FormatNumber類 */
class FormatNumber implements FormatterInterface {
}
複製代碼

適配器模式

適配器模式(Adapter Pattern)是做爲兩個不兼容的接口之間的橋樑。這種類型的設計模式屬於結構型模式,它結合了兩個獨立接口的功能。
這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。舉個真實的例子,讀卡器是做爲內存卡和筆記本之間的適配器。您將內存卡插入讀卡器,再將讀卡器插入筆記本,這樣就能夠經過筆記原本讀取內存卡。

介紹

意圖: 將一個類的接口轉換成客戶但願的另一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。

主要解決: 主要解決在軟件系統中,經常要將一些 "現存的對象" 放到新的環境中,而新環境要求的接口是現對象不能知足的。

什麼時候使用:

  1. 系統須要使用現有的類,而此類的接口不符合系統的須要。
  2. 想要創建一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做,這些源類不必定有一致的接口。
  3. 經過接口轉換,將一個類插入另外一個類系中。(好比老虎和飛禽,如今多了一個飛虎,在不增長實體的需求下,增長一個適配器,在裏面包容一個虎對象,實現飛的接口。)

如何解決: 繼承或依賴(推薦)。

關鍵代碼: 適配器繼承或依賴已有的對象,實現想要的目標接口。

應用實例:

  1. 美國電器 110V,中國 220V,就要有一個適配器將 110V 轉化爲 220V。
  2. JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,則要將之前系統的 Enumeration 接口轉化爲 Iterator 接口,這時就須要適配器模式。
  3. 在 LINUX 上運行 WINDOWS 程序。 4. JAVA 中的 jdbc。

優勢:

  1. 可讓任何兩個沒有關聯的類一塊兒運行。
  2. 提升了類的複用。
  3. 增長了類的透明度。
  4. 靈活性好。

缺點:

  1. 過多地使用適配器,會讓系統很是零亂,不易總體進行把握。好比,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統若是太多出現這種狀況,無異於一場災難。所以若是不是頗有必要,能夠不使用適配器,而是直接對系統進行重構。
  2. 因爲 JAVA 至多繼承一個類,因此至多隻能適配一個適配者類,並且目標類必須是抽象類。

使用場景: 有動機地修改一個正常運行的系統的接口,這時應該考慮使用適配器模式。

注意事項: 適配器不是在詳細設計時添加的,而是解決正在服役的項目的問題。

橋接模式

橋接(Bridge)是用於把抽象化與實現化解耦,使得兩者能夠獨立變化。這種類型的設計模式屬於結構型模式,它經過提供抽象化和實現化之間的橋接結構,來實現兩者的解耦。
這種模式涉及到一個做爲橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。

介紹

意圖: 將抽象部分與實現部分分離,使它們均可以獨立的變化。

主要解決: 在有多種可能會變化的狀況下,用繼承會形成類爆炸問題,擴展起來不靈活。

什麼時候使用: 實現系統可能有多個角度分類,每一種角度均可能變化。

如何解決: 把這種多角度分類分離出來,讓它們獨立變化,減小它們之間耦合。

關鍵代碼: 抽象類依賴實現類。

應用實例:

  1. 豬八戒從天蓬元帥轉世投胎到豬,轉世投胎的機制將塵世劃分爲兩個等級,即:靈魂和肉體,前者至關於抽象化,後者至關於實現化。生靈經過功能的委派,調用肉體對象的功能,使得生靈能夠動態地選擇。
  2. 牆上的開關,能夠看到的開關是抽象的,不用管裏面具體怎麼實現的。

優勢:

  1. 抽象和實現的分離。
  2. 優秀的擴展能力。
  3. 實現細節對客戶透明。

缺點: 橋接模式的引入會增長系統的理解與設計難度,因爲聚合關聯關係創建在抽象層,要求開發者針對抽象進行設計與編程。

使用場景:

  1. 若是一個系統須要在構件的抽象化角色和具體化角色之間增長更多的靈活性,避免在兩個層次之間創建靜態的繼承聯繫,經過橋接模式可使它們在抽象層創建一個關聯關係。
  2. 對於那些不但願使用繼承或由於多層次繼承致使系統類的個數急劇增長的系統,橋接模式尤其適用。
  3. 一個類存在兩個獨立變化的維度,且這兩個維度都須要進行擴展。

注意事項: 對於兩個獨立變化的維度,使用橋接模式再適合不過了。

過濾器模式

過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種設計模式,這種模式容許開發人員使用不一樣的標準來過濾一組對象,經過邏輯運算以解耦的方式把它們鏈接起來。這種類型的設計模式屬於結構型模式,它結合多個標準來得到單一標準。

組合模式

組合模式(Composite Pattern),又叫部分總體模式,是用於把一組類似的對象看成一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及總體層次。這種類型的設計模式屬於結構型模式,它建立了對象組的樹形結構。
這種模式建立了一個包含本身對象組的類。該類提供了修改相同對象組的方式。

介紹

意圖: 將對象組合成樹形結構以表示 "部分 - 總體" 的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。

主要解決: 它在咱們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程序能夠向處理簡單元素同樣來處理複雜元素,從而使得客戶程序與複雜元素的內部結構解耦。

什麼時候使用:

  1. 您想表示對象的部分 - 總體層次結構(樹形結構)。
  2. 您但願用戶忽略組合對象與單個對象的不一樣,用戶將統一地使用組合結構中的全部對象。

如何解決: 樹枝和葉子實現統一接口,樹枝內部組合該接口。

關鍵代碼: 樹枝內部組合該接口,而且含有內部屬性 List,裏面放 Component。

應用實例:

  1. 算術表達式包括操做數. 操做符和另外一個操做數,其中,另外一個操做符也能夠是操做數. 操做符和另外一個操做數。
  2. 在 JAVA AWT 和 SWING 中,對於 Button 和 Checkbox 是樹葉,Container 是樹枝。

優勢:

  1. 高層模塊調用簡單。
  2. 節點自由增長。

缺點: 在使用組合模式時,其葉子和樹枝的聲明都是實現類,而不是接口,違反了依賴倒置原則。

使用場景: 部分. 總體場景,如樹形菜單,文件. 文件夾的管理。

注意事項: 定義時爲具體類。

裝飾器模式

裝飾器模式(Decorator Pattern)容許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是做爲現有的類的一個包裝。
這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。

介紹

意圖: 動態地給一個對象添加一些額外的職責。就增長功能來講,裝飾器模式相比生成子類更爲靈活。

主要解決: 通常的,咱們爲了擴展一個類常用繼承方式實現,因爲繼承爲類引入靜態特徵,而且隨着擴展功能的增多,子類會很膨脹。

什麼時候使用: 在不想增長不少子類的狀況下擴展類。

如何解決: 將具體功能職責劃分,同時繼承裝飾者模式。

關鍵代碼:

  1. Component 類充當抽象角色,不該該具體實現。
  2. 修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。

應用實例:

  1. 孫悟空有 72 變,當他變成 "廟宇" 後,他的根本仍是一隻猴子,可是他又有了廟宇的功能。
  2. 不論一幅畫有沒有畫框均可以掛在牆上,可是一般都是有畫框的,而且其實是畫框被掛在牆上。在掛在牆上以前,畫能夠被蒙上玻璃,裝到框子裏;這時畫、玻璃和畫框造成了一個物體。

優勢: 裝飾類和被裝飾類能夠獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。

缺點: 多層裝飾比較複雜。

使用場景:

  1. 擴展一個類的功能。
  2. 動態增長功能,動態撤銷。

注意事項: 可代替繼承。

外觀模式

外觀模式(Facade Pattern)隱藏系統的複雜性,並向客戶端提供了一個客戶端能夠訪問系統的接口。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個接口,來隱藏系統的複雜性。
這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委託調用。

介紹

意圖: 爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

主要解決: 下降訪問複雜系統的內部子系統時的複雜度,簡化客戶端與之的接口。

什麼時候使用:

  1. 客戶端不須要知道系統內部的複雜聯繫,整個系統只需提供一個 "接待員" 便可。
  2. 定義系統的入口。

如何解決: 客戶端不與系統耦合,外觀類與系統耦合。

關鍵代碼: 在客戶端和複雜系統之間再加一層,這一層將調用順序. 依賴關係等處理好。

應用實例:

  1. 去醫院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家眷以爲很複雜,若是有提供接待人員,只讓接待人員來處理,就很方便。
  2. JAVA 的三層開發模式。

優勢:

  1. 減小系統相互依賴。
  2. 提升靈活性。
  3. 提升了安全性。

缺點: 不符合開閉原則,若是要改東西很麻煩,繼承重寫都不合適。

使用場景:

  1. 爲複雜的模塊或子系統提供外界訪問的模塊。
  2. 子系統相對獨立。
  3. 預防低水平人員帶來的風險。

注意事項: 在層次化結構中,可使用外觀模式定義系統中每一層的入口。

享元模式

享元模式(Flyweight Pattern)主要用於減小建立對象的數量,以減小內存佔用和提升性能。這種類型的設計模式屬於結構型模式,它提供了減小對象數量從而改善應用所需的對象結構的方式。
享元模式嘗試重用現有的同類對象,若是未找到匹配的對象,則建立新對象。咱們將經過建立 5 個對象來畫出 20 個分佈於不一樣位置的圓來演示這種模式。因爲只有 5 種可用的顏色,因此 color 屬性被用來檢查現有的 Circle 對象。

介紹

意圖: 運用共享技術有效地支持大量細粒度的對象。

主要解決: 在有大量對象時,有可能會形成內存溢出,咱們把其中共同的部分抽象出來,若是有相同的業務請求,直接返回在內存中已有的對象,避免從新建立。

什麼時候使用:

  1. 系統中有大量對象。
  2. 這些對象消耗大量內存。
  3. 這些對象的狀態大部分能夠外部化。
  4. 這些對象能夠按照內蘊狀態分爲不少組,當把外蘊對象從對象中剔除出來時,每一組對象均可以用一個對象來代替。
  5. 系統不依賴於這些對象身份,這些對象是不可分辨的。

如何解決: 用惟一標識碼判斷,若是在內存中有,則返回這個惟一標識碼所標識的對象。

關鍵代碼: 用 HashMap 存儲這些對象。

應用實例:

  1. JAVA 中的 String,若是有則返回,若是沒有則建立一個字符串保存在字符串緩存池裏面。2. 數據庫的數據池。

優勢: 大大減小對象的建立,下降系統的內存,使效率提升。

缺點: 提升了系統的複雜度,須要分離出外部狀態和內部狀態,並且外部狀態具備固有化的性質,不該該隨着內部狀態的變化而變化,不然會形成系統的混亂。

使用場景:

  1. 系統有大量類似對象。
  2. 須要緩衝池的場景。

注意事項:

  1. 注意劃分外部狀態和內部狀態,不然可能會引發線程安全問題。
  2. 這些類必須有一個工廠對象加以控制。

代理模式

在代理模式(Proxy Pattern)中,一個類表明另外一個類的功能。這種類型的設計模式屬於結構型模式。
在代理模式中,咱們建立具備現有對象的對象,以便向外界提供功能接口。

介紹

意圖: 爲其餘對象提供一種代理以控制對這個對象的訪問。

主要解決: 在直接訪問對象時帶來的問題,好比說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象因爲某些緣由(好比對象建立開銷很大,或者某些操做須要安全控制,或者須要進程外的訪問),直接訪問會給使用者或者系統結構帶來不少麻煩,咱們能夠在訪問此對象時加上一個對此對象的訪問層。

什麼時候使用: 想在訪問一個類時作一些控制。

如何解決: 增長中間層。

關鍵代碼: 實現與被代理類組合。

應用實例:

  1. Windows 裏面的快捷方式。
  2. 豬八戒去找高翠蘭結果是孫悟空變的,能夠這樣理解:把高翠蘭的外貌抽象出來,高翠蘭本人和孫悟空都實現了這個接口,豬八戒訪問高翠蘭的時候看不出來這個是孫悟空,因此說孫悟空是高翠蘭代理類。
  3. 買火車票不必定在火車站買,也能夠去代售點。
  4. 一張支票或銀行存單是帳戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人帳號上資金的控制。
  5. spring aop。

優勢:

  1. 職責清晰。
  2. 高擴展性。
  3. 智能化。

缺點:

  1. 因爲在客戶端和真實主題之間增長了代理對象,所以有些類型的代理模式可能會形成請求的處理速度變慢。
  2. 實現代理模式須要額外的工做,有些代理模式的實現很是複雜。

使用場景: 按職責來劃分,一般有如下使用場景:

  1. 遠程代理。
  2. 虛擬代理。
  3. Copy-on-Write 代理。
  4. 保護(Protect or Access)代理。
  5. Cache 代理。
  6. 防火牆(Firewall)代理。
  7. 同步化(Synchronization)代理。
  8. 智能引用(Smart Reference)代理。

注意事項:

  1. 和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。
  2. 和裝飾器模式的區別:裝飾器模式爲了加強功能,而代理模式是爲了加以控制。

數據映射模式

在瞭解數據映射模式以前,先了解下數據映射,它是在持久化數據存儲層(一般是關係型數據庫)和駐於內存的數據表現層之間進行雙向數據傳輸的數據訪問層。
數據映射模式的目的是讓持久化數據存儲層、駐於內存的數據表現層、以及數據映射自己三者相互獨立、互不依賴。這個數據訪問層由一個或多個映射器(或者數據訪問對象)組成,用於實現數據傳輸。通用的數據訪問層能夠處理不一樣的實體類型,而專用的則處理一個或幾個。
數據映射模式的核心在於它的數據模型遵循單一職責原則(Single Responsibility Principle), 這也是和 Active Record 模式的不一樣之處。最典型的數據映射模式例子就是數據庫 ORM 模型 (Object Relational Mapper)。
準確來講該模式是個架構模式。

依賴注入模式

依賴注入(Dependency Injection)是控制反轉(Inversion of Control)的一種實現方式。
咱們先來看看什麼是控制反轉。
當調用者須要被調用者的協助時,在傳統的程序設計過程當中,一般由調用者來建立被調用者的實例,但在這裏,建立被調用者的工做再也不由調用者來完成,而是將被調用者的建立移到調用者的外部,從而反轉被調用者的建立,消除了調用者對被調用者建立的控制,所以稱爲控制反轉。
要實現控制反轉,一般的解決方案是將建立被調用者實例的工做交由 IoC 容器來完成,而後在調用者中注入被調用者(經過構造器/方法注入實現),這樣咱們就實現了調用者與被調用者的解耦,該過程被稱爲依賴注入。
依賴注入不是目的,它是一系列工具和手段,最終的目的是幫助咱們開發出鬆散耦合(loose coupled)、可維護、可測試的代碼和程序。這條原則的作法是你們熟知的面向接口,或者說是面向抽象編程。

門面模式

門面模式(Facade)又稱外觀模式,用於爲子系統中的一組接口提供一個一致的界面。門面模式定義了一個高層接口,這個接口使得子系統更加容易使用:引入門面角色以後,用戶只須要直接與門面角色交互,用戶與子系統之間的複雜關係由門面角色來實現,從而下降了系統的耦合度。

示例代碼

Facade.php

<?php

namespace DesignPatterns\Structural\Facade;

/** * 門面類 */
class Facade {
    /** * @var OsInterface */
    protected $os;

    /** * @var BiosInterface */
    protected $bios;

    /** * This is the perfect time to use a dependency injection container * to create an instance of this class * * @param BiosInterface $bios * @param OsInterface $os */
    public function __construct(BiosInterface $bios, OsInterface $os) {
        $this->bios = $bios;
        $this->os = $os;
    }

    /** * turn on the system */
    public function turnOn() {
        $this->bios->execute();
        $this->bios->waitForKeyPress();
        $this->bios->launch($this->os);
    }

    /** * turn off the system */
    public function turnOff() {
        $this->os->halt();
        $this->bios->powerDown();
    }
}
複製代碼

OsInterface.php

<?php

namespace DesignPatterns\Structural\Facade;

/** * OsInterface接口 */
interface OsInterface {
    /** * halt the OS */
    public function halt();
}
複製代碼

BiosInterface.php

<?php

namespace DesignPatterns\Structural\Facade;

/** * BiosInterface接口 */
interface BiosInterface {
    /** * execute the BIOS */
    public function execute();

    /** * wait for halt */
    public function waitForKeyPress();

    /** * launches the OS * * @param OsInterface $os */
    public function launch(OsInterface $os);

    /** * power down BIOS */
    public function powerDown();
}
複製代碼

流接口模式

在軟件工程中,流接口(Fluent Interface)是指實現一種面向對象的、能提升代碼可讀性的 API 的方法,其目的就是能夠編寫具備天然語言同樣可讀性的代碼,咱們對這種代碼編寫方式還有一個通俗的稱呼 —— 方法鏈。
Laravel 中流接口模式有着普遍使用,好比查詢構建器,郵件等等。

示例代碼

Sql.php

<?php

namespace DesignPatterns\Structural\FluentInterface;

/** * SQL 類 */
class Sql {
    /** * @var array */
    protected $fields = array();

    /** * @var array */
    protected $from = array();

    /** * @var array */
    protected $where = array();

    /** * 添加 select 字段 * * @param array $fields * * @return SQL */
    public function select(array $fields = array()) {
        $this->fields = $fields;

        return $this;
    }

    /** * 添加 FROM 子句 * * @param string $table * @param string $alias * * @return SQL */
    public function from($table, $alias) {
        $this->from[] = $table . ' AS ' . $alias;

        return $this;
    }

    /** * 添加 WHERE 條件 * * @param string $condition * * @return SQL */
    public function where($condition) {
        $this->where[] = $condition;

        return $this;
    }

    /** * 生成查詢語句 * * @return string */
    public function getQuery() {
        return 'SELECT ' . implode(',', $this->fields)
                . ' FROM ' . implode(',', $this->from)
                . ' WHERE ' . implode(' AND ', $this->where);
    }
}
複製代碼

註冊模式

註冊模式(Registry)也叫作註冊樹模式,註冊器模式。註冊模式爲應用中常用的對象建立一箇中央存儲器來存放這些對象 —— 一般經過一個只包含靜態方法的抽象類來實現(或者經過單例模式)。

示例代碼

Registry.php

<?php

namespace DesignPatterns\Structural\Registry;

/** * class Registry */
abstract class Registry {
    const LOGGER = 'logger';

    /** * @var array */
    protected static $storedValues = array();

    /** * sets a value * * @param string $key * @param mixed $value * * @static * @return void */
    public static function set($key, $value) {
        self::$storedValues[$key] = $value;
    }

    /** * gets a value from the registry * * @param string $key * * @static * @return mixed */
    public static function get($key) {
        return self::$storedValues[$key];
    }

    // typically there would be methods to check if a key has already been registered and so on ...
}
複製代碼

責任鏈模式

顧名思義,責任鏈模式(Chain of Responsibility Pattern)爲請求建立了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬於行爲型模式。
在這種模式中,一般每一個接收者都包含對另外一個接收者的引用。若是一個對象不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。

介紹

意圖: 避免請求發送者與接收者耦合在一塊兒,讓多個對象都有可能接收請求,將這些對象鏈接成一條鏈,而且沿着這條鏈傳遞請求,直到有對象處理它爲止。

主要解決: 職責鏈上的處理者負責處理請求,客戶只須要將請求發送到職責鏈上便可,無須關心請求的處理細節和請求的傳遞,因此職責鏈將請求的發送者和請求的處理者解耦了。

什麼時候使用: 在處理消息的時候以過濾不少道。

如何解決: 攔截的類都實現統一接口。

關鍵代碼: Handler 裏面聚合它本身,在 HandlerRequest 裏判斷是否合適,若是沒達到條件則向下傳遞,向誰傳遞以前 set 進去。

應用實例:

  1. 紅樓夢中的 "擊鼓傳花"。
  2. JS 中的事件冒泡。
  3. JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。

優勢:

  1. 下降耦合度。它將請求的發送者和接收者解耦。
  2. 簡化了對象。使得對象不須要知道鏈的結構。
  3. 加強給對象指派職責的靈活性。經過改變鏈內的成員或者調動它們的次序,容許動態地新增或者刪除責任。
  4. 增長新的請求處理類很方便。

缺點:

  1. 不能保證請求必定被接收。
  2. 系統性能將受到必定影響,並且在進行代碼調試時不太方便,可能會形成循環調用。
  3. 可能不容易觀察運行時的特徵,有礙於除錯。

使用場景:

  1. 有多個對象能夠處理同一個請求,具體哪一個對象處理該請求由運行時刻自動肯定。
  2. 在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。
  3. 可動態指定一組對象處理請求。

注意事項: 在 JAVA WEB 中遇到不少應用。

命令模式

命令模式(Command Pattern)是一種數據驅動的設計模式,它屬於行爲型模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找能夠處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。

介紹

意圖: 將一個請求封裝成一個對象,從而使您能夠用不一樣的請求對客戶進行參數化。

主要解決: 在軟件系統中,行爲請求者與行爲實現者一般是一種緊耦合的關係,但某些場合,好比須要對行爲進行記錄、撤銷或重作、事務等處理時,這種沒法抵禦變化的緊耦合的設計就不太合適。

什麼時候使用: 在某些場合,好比要對行爲進行 "記錄、撤銷 / 重作、事務" 等處理,這種沒法抵禦變化的緊耦合是不合適的。在這種狀況下,如何將 "行爲請求者" 與 "行爲實現者" 解耦?將一組行爲抽象爲對象,能夠實現兩者之間的鬆耦合。

如何解決: 經過調用者調用接受者執行命令,順序:調用者→接受者→命令。

關鍵代碼: 定義三個角色:

  1. received 真正的命令執行對象
  2. Command
  3. invoker 使用命令對象的入口

應用實例: struts 1 中的 action 核心控制器 ActionServlet 只有一個,至關於 Invoker,而模型層的類會隨着不一樣的應用有不一樣的模型類,至關於具體的 Command。

優勢:

  1. 下降了系統耦合度。
  2. 新的命令能夠很容易添加到系統中去。

缺點: 使用命令模式可能會致使某些系統有過多的具體命令類。

使用場景: 認爲是命令的地方均可以使用命令模式,好比:

  1. GUI 中每個按鈕都是一條命令。
  2. 模擬 CMD。

注意事項: 系統須要支持命令的撤銷 (Undo) 操做和恢復 (Redo) 操做,也能夠考慮使用命令模式,見命令模式的擴展。

解釋器模式

解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行爲型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

介紹

意圖: 給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。

主要解決: 對於一些固定文法構建一個解釋句子的解釋器。

什麼時候使用: 若是一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述爲一個簡單語言中的句子。這樣就能夠構建一個解釋器,該解釋器經過解釋這些句子來解決該問題。

如何解決: 構建語法樹,定義終結符與非終結符。

關鍵代碼: 構建環境類,包含解釋器以外的一些全局信息,通常是 HashMap。

應用實例: 編譯器、運算表達式計算。

優勢:

  1. 可擴展性比較好,靈活。
  2. 增長了新的解釋表達式的方式。
  3. 易於實現簡單文法。

缺點:

  1. 可利用場景比較少。
  2. 對於複雜的文法比較難維護。
  3. 解釋器模式會引發類膨脹。
  4. 解釋器模式採用遞歸調用方法。

使用場景:

  1. 能夠將一個須要解釋執行的語言中的句子表示爲一個抽象語法樹。
  2. 一些重複出現的問題能夠用一種簡單的語言來進行表達。
  3. 一個簡單語法須要解釋的場景。

注意事項: 可利用場景比較少,JAVA 中若是碰到能夠用 expression4J 代替。

迭代器模式

迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環境中很是經常使用的設計模式。這種模式用於順序訪問集合對象的元素,不須要知道集合對象的底層表示。
迭代器模式屬於行爲型模式。

介紹

意圖: 提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示。

主要解決: 不一樣的方式來遍歷整個整合對象。

什麼時候使用: 遍歷一個聚合對象。

如何解決: 把在元素之間遊走的責任交給迭代器,而不是聚合對象。

關鍵代碼: 定義接口:hasNext, next。

應用實例: JAVA 中的 iterator。

優勢:

  1. 它支持以不一樣的方式遍歷一個聚合對象。
  2. 迭代器簡化了聚合類。
  3. 在同一個聚合上能夠有多個遍歷。
  4. 在迭代器模式中,增長新的聚合類和迭代器類都很方便,無須修改原有代碼。

缺點: 因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,這在必定程度上增長了系統的複雜性。

使用場景:

  1. 訪問一個聚合對象的內容而無須暴露它的內部表示。
  2. 須要爲聚合對象提供多種遍歷方式。
  3. 爲遍歷不一樣的聚合結構提供一個統一的接口。

注意事項: 迭代器模式就是分離了集合對象的遍歷行爲,抽象出一個迭代器類來負責,這樣既能夠作到不暴露集合的內部結構,又可以讓外部代碼透明地訪問集合內部的數據。

中介者模式

中介者模式(Mediator Pattern)是用來下降多個對象和類之間的通訊複雜性。這種模式提供了一箇中介類,該類一般處理不一樣類之間的通訊,並支持鬆耦合,使代碼易於維護。中介者模式屬於行爲型模式。

介紹

意圖: 用一箇中介對象來封裝一系列的對象交互,中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。

主要解決: 對象與對象之間存在大量的關聯關係,這樣勢必會致使系統的結構變得很複雜,同時若一個對象發生改變,咱們也須要跟蹤與之相關聯的對象,同時作出相應的處理。

什麼時候使用: 多個類相互耦合,造成了網狀結構。

如何解決: 將上述網狀結構分離爲星型結構。

關鍵代碼: 對象 Colleague 之間的通訊封裝到一個類中單獨處理。

應用實例:

  1. 中國加入 WTO 以前是各個國家相互貿易,結構複雜,如今是各個國家經過 WTO 來互相貿易。
  2. 機場調度系統。
  3. MVC 框架,其中 C(控制器)就是 M(模型)和 V(視圖)的中介者。

優勢:

  1. 下降了類的複雜度,將一對多轉化成了一對一。
  2. 各個類之間的解耦。
  3. 符合迪米特原則。

缺點: 中介者會龐大,變得複雜難以維護。

使用場景:

  1. 系統中對象之間存在比較複雜的引用關係,致使它們之間的依賴關係結構混亂並且難以複用該對象。
  2. 想經過一箇中間類來封裝多個類中的行爲,而又不想生成太多的子類。

注意事項: 不該當在職責混亂的時候使用。

備忘錄模式

備忘錄模式(Memento Pattern)保存一個對象的某個狀態,以便在適當的時候恢復對象。備忘錄模式屬於行爲型模式。

介紹

意圖: 在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。

主要解決: 所謂備忘錄模式就是在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態,這樣能夠在之後將對象恢復到原先保存的狀態。

什麼時候使用: 不少時候咱們老是須要記錄一個對象的內部狀態,這樣作的目的就是爲了容許用戶取消不肯定或者錯誤的操做,可以恢復到他原先的狀態,使得他有 "後悔藥" 可吃。

如何解決: 經過一個備忘錄類專門存儲對象狀態。

關鍵代碼: 客戶不與備忘錄類耦合,與備忘錄管理類耦合。

應用實例:

  1. 後悔藥。
  2. 打遊戲時的存檔。
  3. Windows 裏的 ctri + z。
  4. IE 中的後退。
  5. 數據庫的事務管理。

優勢:

  1. 給用戶提供了一種能夠恢復狀態的機制,可使用戶可以比較方便地回到某個歷史的狀態。
  2. 實現了信息的封裝,使得用戶不須要關心狀態的保存細節。

缺點: 消耗資源。若是類的成員變量過多,勢必會佔用比較大的資源,並且每一次保存都會消耗必定的內存。

使用場景:

  1. 須要保存 / 恢復數據的相關狀態場景。
  2. 提供一個可回滾的操做。

注意事項:

  1. 爲了符合迪米特原則,還要增長一個管理備忘錄的類。
  2. 爲了節約內存,可以使用原型模式 + 備忘錄模式。

觀察者模式

當對象間存在一對多關係時,則使用觀察者模式(Observer Pattern)。好比,當一個對象被修改時,則會自動通知它的依賴對象。觀察者模式屬於行爲型模式。

介紹

意圖: 定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。

主要解決: 一個對象狀態改變給其餘對象通知的問題,並且要考慮到易用和低耦合,保證高度的協做。

什麼時候使用: 一個對象(目標對象)的狀態發生改變,全部的依賴對象(觀察者對象)都將獲得通知,進行廣播通知。

如何解決: 使用面向對象技術,能夠將這種依賴關係弱化。

關鍵代碼: 在抽象類裏有一個 ArrayList 存放觀察者們。

應用實例:

  1. 拍賣的時候,拍賣師觀察最高標價,而後通知給其餘競價者競價。
  2. 西遊記裏面悟空請求菩薩降服紅孩兒,菩薩灑了一地水招來一個老烏龜,這個烏龜就是觀察者,他觀察菩薩灑水這個動做。

優勢:

  1. 觀察者和被觀察者是抽象耦合的。
  2. 創建一套觸發機制。

缺點:

  1. 若是一個被觀察者對象有不少的直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間。
  2. 若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰。
  3. 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

使用場景:

  1. 一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面。將這些方面封裝在獨立的對象中使它們能夠各自獨立地改變和複用。
  2. 一個對象的改變將致使其餘一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,能夠下降對象之間的耦合度。
  3. 一個對象必須通知其餘對象,而並不知道這些對象是誰。
  4. 須要在系統中建立一個觸發鏈,A 對象的行爲將影響 B 對象,B 對象的行爲將影響 C 對象……,可使用觀察者模式建立一種鏈式觸發機制。

注意事項:

  1. JAVA 中已經有了對觀察者模式的支持類。
  2. 避免循環引用。
  3. 若是順序執行,某一觀察者錯誤會致使系統卡殼,通常採用異步方式。

狀態模式

在狀態模式(State Pattern)中,類的行爲是基於它的狀態改變的。這種類型的設計模式屬於行爲型模式。
在狀態模式中,咱們建立表示各類狀態的對象和一個行爲隨着狀態對象改變而改變的 context 對象。

介紹

意圖: 容許對象在內部狀態發生改變時改變它的行爲,對象看起來好像修改了它的類。

主要解決: 對象的行爲依賴於它的狀態(屬性),而且能夠根據它的狀態改變而改變它的相關行爲。

什麼時候使用: 代碼中包含大量與對象狀態有關的條件語句。

如何解決: 將各類具體的狀態類抽象出來。

關鍵代碼: 一般命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。並且,狀態模式的實現類的方法,通常返回值,或者是改變實例變量的值。也就是說,狀態模式通常和對象的狀態有關。實現類的方法有不一樣的功能,覆蓋接口中的方法。狀態模式和命令模式同樣,也能夠用於消除 if...else 等條件選擇語句。

應用實例:

  1. 打籃球的時候運動員能夠有正常狀態. 不正常狀態和超常狀態。
  2. 曾侯乙編鐘中,'鍾是抽象接口','鍾 A'等是具體狀態,'曾侯乙編鐘'是具體環境(Context)。

優勢:

  1. 封裝了轉換規則。
  2. 枚舉可能的狀態,在枚舉狀態以前須要肯定狀態種類。
  3. 將全部與某個狀態有關的行爲放到一個類中,而且能夠方便地增長新的狀態,只須要改變對象狀態便可改變對象的行爲。
  4. 容許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
  5. 可讓多個環境對象共享一個狀態對象,從而減小系統中對象的個數。

缺點:

  1. 狀態模式的使用必然會增長系統類和對象的個數。
  2. 狀態模式的結構與實現都較爲複雜,若是使用不當將致使程序結構和代碼的混亂。
  3. 狀態模式對 "開閉原則" 的支持並不太好,對於能夠切換狀態的狀態模式,增長新的狀態類須要修改那些負責狀態轉換的源代碼,不然沒法切換到新增狀態,並且修改某個狀態類的行爲也需修改對應類的源代碼。

使用場景:

  1. 行爲隨狀態改變而改變的場景。
  2. 條件、分支語句的代替者。

注意事項: 在行爲受狀態約束的時候使用狀態模式,並且狀態不超過 5 個。

空對象模式

在空對象模式(Null Object Pattern)中,一個空對象取代 NULL 對象實例的檢查。Null 對象不是檢查空值,而是反應一個不作任何動做的關係。這樣的 Null 對象也能夠在數據不可用的時候提供默認的行爲。
在空對象模式中,咱們建立一個指定各類要執行的操做的抽象類和擴展該類的實體類,還建立一個未對該類作任何實現的空對象類,該空對象類將無縫地使用在須要檢查空值的地方。

策略模式

在策略模式(Strategy Pattern)中,一個類的行爲或其算法能夠在運行時更改。這種類型的設計模式屬於行爲型模式。
在策略模式中,咱們建立表示各類策略的對象和一個行爲隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。

介紹

意圖: 定義一系列的算法, 把它們一個個封裝起來, 而且使它們可相互替換。

主要解決: 在有多種算法類似的狀況下,使用 if...else 所帶來的複雜和難以維護。

什麼時候使用: 一個系統有許多許多類,而區分它們的只是他們直接的行爲。

如何解決: 將這些算法封裝成一個一個的類,任意地替換。

關鍵代碼: 實現同一個接口。

應用實例:

  1. 諸葛亮的錦囊妙計,每個錦囊就是一個策略。
  2. 旅行的出遊方式,選擇騎自行車. 坐汽車,每一種旅行方式都是一個策略。
  3. JAVA AWT 中的 LayoutManager。

優勢:

  1. 算法能夠自由切換。
  2. 避免使用多重條件判斷。
  3. 擴展性良好。

缺點:

  1. 策略類會增多。
  2. 全部策略類都須要對外暴露。

使用場景:

  1. 若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲。
  2. 一個系統須要動態地在幾種算法中選擇一種。
  3. 若是一個對象有不少的行爲,若是不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。

注意事項: 若是一個系統的策略多於四個,就須要考慮使用混合模式,解決策略類膨脹的問題。

模板模式

在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式 / 模板。它的子類能夠按須要重寫方法實現,但調用將以抽象類中定義的方式進行。這種類型的設計模式屬於行爲型模式。

介紹

意圖: 定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

主要解決: 一些方法通用,卻在每個子類都從新寫了這一方法。

什麼時候使用: 有一些通用的方法。

如何解決: 將這些通用算法抽象出來。

關鍵代碼: 在抽象類實現,其餘步驟在子類實現。

應用實例:

  1. 在造房子的時候,地基、走線、水管都同樣,只有在建築的後期纔有加壁櫥加柵欄等差別。 2. 西遊記裏面菩薩定好的 81 難,這就是一個頂層的邏輯骨架。
  2. spring 中對 Hibernate 的支持,將一些已經定好的方法封裝起來,好比開啓事務. 獲取 Session. 關閉 Session 等,程序員不重複寫那些已經規範好的代碼,直接丟一個實體就能夠保存。

優勢:

  1. 封裝不變部分,擴展可變部分。
  2. 提取公共代碼,便於維護。
  3. 行爲由父類控制,子類實現。

缺點: 每個不一樣的實現都須要一個子類來實現,致使類的個數增長,使得系統更加龐大。

使用場景:

  1. 有多個子類共有的方法,且邏輯相同。
  2. 重要的、複雜的方法,能夠考慮做爲模板方法。

注意事項: 爲防止惡意操做,通常模板方法都加上 final 關鍵詞。

訪問者模式

在訪問者模式(Visitor Pattern)中,咱們使用了一個訪問者類,它改變了元素類的執行算法。經過這種方式,元素的執行算法能夠隨着訪問者改變而改變。這種類型的設計模式屬於行爲型模式。根據模式,元素對象已接受訪問者對象,這樣訪問者對象就能夠處理元素對象上的操做。

介紹

意圖: 主要將數據結構與數據操做分離。

主要解決: 穩定的數據結構和易變的操做耦合問題。

什麼時候使用: 須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做 "污染" 這些對象的類,使用訪問者模式將這些封裝到類中。

如何解決: 在被訪問的類裏面加一個對外提供接待訪問者的接口。

關鍵代碼: 在數據基礎類裏面有一個方法接受訪問者,將自身引用傳入訪問者。

應用實例: 您在朋友家作客,您是訪問者,朋友接受您的訪問,您經過朋友的描述,而後對朋友的描述作出一個判斷,這就是訪問者模式。

優勢:

  1. 符合單一職責原則。
  2. 優秀的擴展性。
  3. 靈活性。

缺點:

  1. 具體元素對訪問者公佈細節,違反了迪米特原則。
  2. 具體元素變動比較困難。
  3. 違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。

使用場景:

  1. 對象結構中對象對應的類不多改變,但常常須要在此對象結構上定義新的操做。
  2. 須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做 "污染" 這些對象的類,也不但願在增長新操做時修改這些類。

注意事項: 訪問者能夠對功能進行統一,能夠作報表、UI、攔截器與過濾器。

規格模式

規格模式(Specification)能夠認爲是組合模式的一種擴展。有時項目中某些條件決定了業務邏輯,這些條件就能夠抽離出來以某種關係(與、或、非)進行組合,從而靈活地對業務邏輯進行定製。另外,在查詢、過濾等應用場合中,經過預約義多個條件,而後使用這些條件的組合來處理查詢或過濾,而不是使用邏輯判斷語句來處理,能夠簡化整個實現邏輯。
這裏的每一個條件就是一個規格,多個規格/條件經過串聯的方式以某種邏輯關係造成一個組合式的規格。

訪問者模式

咱們去銀行櫃檯辦業務,通常狀況下會開幾個我的業務櫃檯的,你去其中任何一個櫃檯辦理都是能夠的。咱們的訪問者模式能夠很好付諸在這個場景中:對於銀行櫃檯來講,他們是不用變化的,就是說今天和明天提供我的業務的櫃檯是不須要有變化的。而咱們做爲訪問者,今天來銀行多是取消費流水,明天來銀行多是去辦理手機銀行業務,這些是咱們訪問者的操做,一直是在變化的。
訪問者模式就是表示一個做用於某對象結構中的各元素的操做。它使你能夠在不改變各元素的類的前提下定義做用於這些元素的新操做。


MVC模式

MVC 模式表明 Model-View-Controller(模型 - 視圖 - 控制器) 模式。這種模式用於應用程序的分層開發。

  • Model(模型) - 模型表明一個存取數據的對象或 JAVA POJO。它也能夠帶有邏輯,在數據變化時更新控制器。
  • View(視圖) - 視圖表明模型包含的數據的可視化。
  • Controller(控制器) - 控制器做用於模型和視圖上。它控制數據流向模型對象,並在數據變化時更新視圖。它使視圖與模型分離開。

業務表明模式

業務表明模式(Business Delegate Pattern)用於對錶示層和業務層解耦。它基本上是用來減小通訊或對錶示層代碼中的業務層代碼的遠程查詢功能。在業務層中咱們有如下實體。

  • 客戶端(Client) - 表示層代碼能夠是 JSP、servlet 或 UI java 代碼。
  • 業務表明(Business Delegate) - 一個爲客戶端實體提供的入口類,它提供了對業務服務方法的訪問。
  • 查詢服務(LookUp Service) - 查找服務對象負責獲取相關的業務實現,並提供業務對象對業務表明對象的訪問。
  • 業務服務(Business Service) - 業務服務接口。實現了該業務服務的實體類,提供了實際的業務實現邏輯。

組合實體模式

組合實體模式(Composite Entity Pattern)用在 EJB 持久化機制中。一個組合實體是一個 EJB 實體 bean,表明了對象的圖解。當更新一個組合實體時,內部依賴對象 beans 會自動更新,由於它們是由 EJB 實體 bean 管理的。如下是組合實體 bean 的參與者。

  • 組合實體(Composite Entity) - 它是主要的實體 bean。它能夠是粗粒的,或者能夠包含一個粗粒度對象,用於持續生命週期。
  • 粗粒度對象(Coarse-Grained Object) - 該對象包含依賴對象。它有本身的生命週期,也能管理依賴對象的生命週期。
  • 依賴對象(Dependent Object) - 依賴對象是一個持續生命週期依賴於粗粒度對象的對象。
  • 策略(Strategies) - 策略表示如何實現組合實體。

數據訪問對象模式

數據訪問對象模式(Data Access Object Pattern)或 DAO 模式用於把低級的數據訪問 API 或操做從高級的業務服務中分離出來。如下是數據訪問對象模式的參與者。

  • 數據訪問對象接口(Data Access Object Interface) - 該接口定義了在一個模型對象上要執行的標準操做。
  • 數據訪問對象實體類(Data Access Object concrete class) - 該類實現了上述的接口。該類負責從數據源獲取數據,數據源能夠是數據庫,也能夠是 xml,或者是其餘的存儲機制。
  • 模型對象 / 數值對象(Model Object/Value Object) - 該對象是簡單的 POJO,包含了 get/set 方法來存儲經過使用 DAO 類檢索到的數據。

前端控制器模式

前端控制器模式(Front Controller Pattern)是用來提供一個集中的請求處理機制,全部的請求都將由一個單一的處理程序處理。該處理程序能夠作認證 / 受權 / 記錄日誌,或者跟蹤請求,而後把請求傳給相應的處理程序。如下是這種設計模式的實體。

  • 前端控制器(Front Controller) - 處理應用程序全部類型請求的單個處理程序,應用程序能夠是基於 web 的應用程序,也能夠是基於桌面的應用程序。
  • 調度器(Dispatcher) - 前端控制器可能使用一個調度器對象來調度請求到相應的具體處理程序。
  • 視圖(View) - 視圖是爲請求而建立的對象。

攔截過濾器模式

攔截過濾器模式(Intercepting Filter Pattern)用於對應用程序的請求或響應作一些預處理 / 後處理。定義過濾器,並在把請求傳給實際目標應用程序以前應用在請求上。過濾器能夠作認證 / 受權 / 記錄日誌,或者跟蹤請求,而後把請求傳給相應的處理程序。如下是這種設計模式的實體。

  • 過濾器(Filter) - 過濾器在請求處理程序執行請求以前或以後,執行某些任務。
  • 過濾器鏈(Filter Chain) - 過濾器鏈帶有多個過濾器,並在 Target 上按照定義的順序執行這些過濾器。
  • Target - Target 對象是請求處理程序。
  • 過濾管理器(Filter Manager) - 過濾管理器管理過濾器和過濾器鏈。
  • 客戶端(Client) - Client 是向 Target 對象發送請求的對象。

參考連接

相關文章
相關標籤/搜索