java設計模式彙總

前言

翻譯與github 上 java design 項目,旨在提供java設計模式的速查。

引用
[3]java 設計模式

目錄

1.抽象文檔

標題 類別 難度
abstract-document(抽象文檔) 結構型 中等

意圖


靈活的實現類型安全,與語言無關。

引用[1]

所謂抽象就是:概念的聚合(Aggregate),可以很安全地忽略細節去使用一個概念,在不同的層次處理不同的細節。現實世界中很多東西已經是某種程度上的抽象了,舉個例子:當你開門時,你直接去操縱門把手就行了,你不會去操心門具體用的什麼木質材料、甚至分子層面,否則每天你開門時都將寸步難行。當你沒能正確地抽象時,你的系統就像一扇過度複雜而無法打開的門,而好的開發者則會從門把手、門、房子等(對應軟件開發中的方法、類、包等層次)各個層面創造出正確的抽象來降低複雜度。

適用範圍


  • 迫切的需要添加新屬性
  • 更靈活的處理樹形結構
  • 你想要更鬆散的耦合系統

權威信息


例子實現分析


Java 抽象文檔設計模式例子分析


2.抽象工廠

標題 類別 難度
abstract-factory(抽象文檔) 創建型 中等

意圖


提供一個接口,用於創建相關或從屬的一類對象,而不指定它們的具體類。

解釋


真實世界的例子

創建一個王國,我們需要對象的共同主題。精靈王國需要精靈王,精靈城堡和精靈的軍隊而獸人王國需要一個獸人王,獸人和獸人軍隊的城堡。在王國中對象間的依賴

字面意思

工廠的工廠;把個體和相關的、依賴的工廠組合在一起而不指定具體的工廠。

維基百科

抽象工廠模式提供了一種方法來封裝一組具有共同主題的工廠,而不指定具體的類。

編程例子

翻譯上面的王國例子。首先,我們爲王國中的對象提供了一些接口和實現。

public interface Castle {
  String getDescription();
}
public interface King {
  String getDescription();
}
public interface Army {
  String getDescription();
}

// Elven implementations ->
public class ElfCastle implements Castle {
  static final String DESCRIPTION = "This is the Elven castle!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}
public class ElfKing implements King {
  static final String DESCRIPTION = "This is the Elven king!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}
public class ElfArmy implements Army {
  static final String DESCRIPTION = "This is the Elven Army!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}

// 獸人實現類似

然後我們王國工廠的抽象和實現

public interface KingdomFactory {
  Castle createCastle();
  King createKing();
  Army createArmy();
}

public class ElfKingdomFactory implements KingdomFactory {
  public Castle createCastle() {
    return new ElfCastle();
  }
  public King createKing() {
    return new ElfKing();
  }
  public Army createArmy() {
    return new ElfArmy();
  }
}

public class OrcKingdomFactory implements KingdomFactory {
  public Castle createCastle() {
    return new OrcCastle();
  }
  public King createKing() {
    return new OrcKing();
  }
  public Army createArmy() {
    return new OrcArmy();
  }
}

現在我們有了一個抽象工廠,讓我們製造相關的家族,即精靈王國工廠製造精靈城堡、國王和軍隊等。

KingdomFactory factory = new ElfKingdomFactory();
Castle castle = factory.createCastle();
King king = factory.createKing();
Army army = factory.createArmy();

castle.getDescription();  // Output: This is the Elven castle!
king.getDescription(); // Output: This is the Elven king!
army.getDescription(); // Output: This is the Elven Army!

適用範圍

  • 一個系統應該獨立於它的產品是如何創建、組合和表示。
  • 一個系統應配置一個產品的多種家族
  • 一系列相關產品對象被設計爲一起使用,您需要強制執行此約束。
  • 您希望提供一個產品類庫,並且希望只顯示它們的接口,而不是它們的實現。
  • 依賴的生命週期短於消費的一方。
  • 你需要一個運行時的值來構造一個特殊的依賴
  • 你要決定哪些產品在運行時調用哪種家族.
  • 在解析依賴項之前,您需要提供一個或多個參數,這些參數只在運行時已知。

用例:

  • 選擇要在運行時調用文件服務、數據庫服務或網絡服務適當的實現.
  • 更好的實現單元測試

結論:

  • 依賴注入可以讓java運行時報錯的服務提早在編譯時被抓住.

現實世界的例子

權威信息

3.適配器模式

標題 類別 難度
adapter(適配器) 結構型 GOF,中等

意圖


將一個類的接口轉換成客戶端期待的接口。適配器讓彼此之間接口不兼容的類工作。

解釋


真實例子

你想把內存卡中的圖片傳輸到自己的電腦上. 爲了傳輸它們,你需要一種與你的計算機端口兼容的適配器,這樣你就可以把內存卡附加到你的計算機上。在這種情況下,讀卡器是一個適配器。

另一個例子是著名的電源適配器;一個三條腿的插頭不能連接到一個兩個孔的插座,它需要使用電源適配器,使兩個孔的插座兼容。

另一個例子是譯者將一個人的話翻譯給另一個人。

字面意思

適配器模式允許您在適配器中包裝一個不兼容的對象,使其與另一個類兼容。

維基描述

在軟件工程中,適配器模式是一種軟件設計模式,它允許現有類的接口作爲另一個接口使用。它通常用於使現有類與其他類協同工作而不修改源代碼。

編程例子

考慮一個只能使用划艇的船長,根本不能使用帆船。
首先我們有接口划艇帆船

public interface RowingBoat {
  void row();
}

public class FishingBoat {
  private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
  public void sail() {
    LOGGER.info("The fishing boat is sailing");
  }
}

並且船長希望實現“RowingBoat”接口能夠行駛船

public class Captain implements RowingBoat {

  private RowingBoat rowingBoat;

  public Captain(RowingBoat rowingBoat) {
    this.rowingBoat = rowingBoat;
  }

  @Override
  public void row() {
    rowingBoat.row();
  }
}

現在讓我們說海盜來了,船長需要逃跑,但只有漁船可用。我們需要創建一個適配器,讓船長用他的划船技巧操作漁船。

public class FishingBoatAdapter implements RowingBoat {

  private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);

  private FishingBoat boat;

  public FishingBoatAdapter() {
    boat = new FishingBoat();
  }

  @Override
  public void row() {
    boat.sail();
  }
}

現在船長可以用漁船逃離海盜。

Captain captain = new Captain(new FishingBoatAdapter());
captain.row();

博主思考
比如說一個廚師,他擁有各種各樣的專業知識,比如烤麪包,但是他並不需要知道如何從原材料中獲取食材,他只需要知道如何使用食材。適配器很多時候就像提供資源一樣,提供麪粉,
牛奶,雞蛋,黃油,醬油…也就是適配器跟原材料到可使用的食材打交道,生成廚師需要的食材。好處說白了就是,廚師(領域知識)只需要專精他如何製作更美味更好的食物。而我們(客戶端程序員)只需要提供廚師需要的資源就行了 :)
這裏寫圖片描述

使用範圍


  • 你想使用一個現有的類,它的接口與你所需要的不匹配
  • 你想創建一個可重用的類,與不相關或不可預見的類進行協作,也就是說,不一定具有兼容接口的類
  • 您需要使用幾個現有的子類,但通過對每個子類進行子類化來調整其接口是不切實際的。 對象適配器可以調整其父類的接口
  • 大多數使用第三方庫的應用程序使用適配器作爲應用程序和第三方庫之間的中間層,以將應用程序與庫分離。 如果必須使用另一個庫,則只需要新庫的適配器,而無需更改應用程序代碼。

結論:


類和對象適配器有不同的取捨。一個類適配器

  • 當致力於去適配一個具體的Adaptee (受改造者)類到目標的類。將導致類適配器不能適配一個類和他所有的子類。
  • 讓適配器重寫Adaptee(受改造者)的一些行爲,因爲適配器是Adaptee(受改造者)的子類。
  • 僅引入一個對象到Adaptee(受改造者)中,並不需要使用適配器。

對象適配器

  • 讓我們一個適配器與許多Adaptee工作,即Adaptee本身及其所有子類(如果有的話)。 適配器還可以一次性向所有Adaptees添加功能。
  • 很難覆蓋Adaptee(受改造者)的行爲。這將需要Adaptee(受改造者)的子類和適配器相互作用而不是Adaptee(受改造者)本身

真實例子


權威信息


4.聚合器-微服務設計模式


標題 類別 難度
Aggregator Microservices(聚合器-微服務) 結構型 Java,Spring

Intent


用戶對Aggregator進行單次調用,然後聚合器調用每個相關的微服務並進行收集
數據,將業務邏輯應用於它,並進一步發佈爲REST端點。
聚合器的更多變體:
- 代理微服務設計模式:根據業務需求調用不同的微服務。
- 鏈接的微服務設計模式:在這種情況下,每個微服務依賴/鏈接到一系列
的其他微服務。

適用範圍


當您需要統一的API時,無論客戶端設備有多少種,使用Aggregator Microservices模式。

權威信息

5.api-網關


標題 類別 難度
api-網關 結構型 Java,Spring,中等難度

意圖


聚集微服務調用在一個位置: API 網關. 用戶只調用api網關, Api網關調用其他微服務.

這裏寫圖片描述

適用範圍


當使用API網關模式時

  • 你也需要使用微服務模式,並將它們聚集到一個點,方便調用它們。

權威信息

6.異步方法調用


標題 類別 難度
異步方法調用 併發 Java,中等難度,函數式,響應式

意圖


異步方法調用設計模式的作用是等待任務結果時不被阻塞。這種設模式對多個獨立的任務提供並行處理,並且提供回調(callback)或等待阻塞當前線程(await)拿到結果值。

這裏寫圖片描述

適用範圍


如下情況使用異步調用模式:

  • 您有多個可以並行運行的獨立任務
  • 您需要提高一組順序任務的性能
  • 您的處理能力有限制或有一個長時間運行的任務,等待結果的一方(caller)不應該長時間等待

真實例子

7.阻塞模式


標題 類別 難度
阻塞模式(Balking Pattern) 併發 Java,中等難度

意圖


阻塞模式被用於阻止某個對象執行某些不完全或不適當狀態的代碼

這裏寫圖片描述

適用範圍


如下情況使用阻塞模式:

  • 只有當某個對象處於特定狀態時,才能夠調用對象的操作
  • 對象通常處於暫時癱瘓的狀態在一段未知的時間內。

相關模式

  • Guarded Suspension Pattern
  • Double Checked Locking Pattern(雙重鎖檢查單例模式)

8.橋接模式


標題 類別 難度
橋接模式(Bridge Pattern) 結構型 Java,GOF,中等難度

意圖


將抽象(abstraction)與它的實現(implementation)解耦,使兩者可以獨立變化。

解釋


真實例子

考慮你有一個具有魔法的武器,你應該允許不同的武器擁有不同的魔法。你會怎麼做? 創建多個武器副本賦予每個武器副本魔法或者創建單獨魔法類並將其設置到武器類,橋接模式允許你實現第二種方式

字面意義

橋接模式更喜歡組合相對於繼承. 實現細節從一個層次結構轉移到另一個具有單獨層次結構的對象

維基百科

橋接模式是一種用於軟件工程的設計模式,其目的是“將抽象與實現分離,從而使兩者獨立變化”。

編程例子

從上面翻譯我們的武器示例。這裏我們有Weapon

public interface Weapon {
  void wield();
  void swing();
  void unwield();
  Enchantment getEnchantment();
}

public class Sword implements Weapon {

  private final Enchantment enchantment;

  public Sword(Enchantment enchantment) {
    this.enchantment = enchantment;
  }

  @Override
  public void wield() {
    LOGGER.info("The sword is wielded.");
    enchantment.onActivate();
  }

  @Override
  public void swing() {
    LOGGER.info("The sword is swinged.");
    enchantment.apply();
  }

  @Override
  public void unwield() {
    LOGGER.info("The sword is unwielded.");
    enchantment.onDeactivate();
  }

  @Override
  public Enchantment getEnchantment() {
    return enchantment;
  }
}

public class Hammer implements Weapon {

  private final Enchantment enchantment;

  public Hammer(Enchantment enchantment) {
    this.enchantment = enchantment;
  }

  @Override
  public void wield() {
    LOGGER.info("The hammer is wielded.");
    enchantment.onActivate();
  }

  @Override
  public void swing() {
    LOGGER.info("The hammer is swinged.");
    enchantment.apply();
  }

  @Override
  public void unwield() {
    LOGGER.info("The hammer is unwielded.");
    enchantment.onDeactivate();
  }

  @Override
  public Enchantment getEnchantment() {
    return enchantment;
  }
}

分離的施法層

public interface Enchantment {
  void onActivate();
  void apply();
  void onDeactivate();
}

public class FlyingEnchantment implements Enchantment {

  @Override
  public void onActivate() {
    LOGGER.info("The item begins to glow faintly.");
  }

  @Override
  public void apply() {
    LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
  }

  @Override
  public void onDeactivate() {
    LOGGER.info("The item's glow fades.");
  }
}

public class SoulEatingEnchantment implements Enchantment {

  @Override
  public void onActivate() {
    LOGGER.info("The item spreads bloodlust.");
  }

  @Override
  public void apply() {
    LOGGER.info("The item eats the soul of enemies.");
  }

  @Override
  public void onDeactivate() {
    LOGGER.info("Bloodlust slowly disappears.");
  }
}

action在兩個層次之間

Sword enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
// 劍被拿起.
// 開始殺戮.
// 劍被使用.
// 這個魔法吞噬敵人的靈魂。.
// 劍被放下.
// 嗜血慢慢消失.

Hammer hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();

適用範圍


使用Bridge模式

  • 你想避免抽象和它的實現之間的永久綁定。可能是這種情況,例如,必須在運行時選擇或切換實現。
  • 抽象和它們的實現都應該通過子類化來擴展。在這種情況下,Bridge模式可以讓您組合不同的抽象和實現,並獨立擴展它們
  • 抽象實現的變化對客戶不應有影響;也就是說,他們的代碼不應該被重新編譯。
  • 你有一個基類有太多子類。這樣的類層次結構表示需要將對象分成兩部分。 Rumbaugh使用術語“嵌套泛化”來引用這樣的類層次結構
  • 您希望在多個對象之間共享一個實現(可能使用引用計數),並且該事實應該從客戶端隱藏。一個簡單的例子是Coplien的String類,其中多個對象可以共享相同的字符串表示。

權威信息


9.構建模式


標題 類別 難度
構建模式(Builder Pattern) 構建型 Java,GOF,中等難度

意圖


將複雜對象的構造與它的表現分離,使相同的構建過程可以創建不同的表現。

解釋


真實例子

想象一個角色扮演遊戲的角色生成器。最簡單的選擇是讓計算機爲你創建角色。但是,如果你想選擇諸如職業,性別,頭髮顏色等字符的細節,角色生成器成爲一個循序漸進的過程,完成時,所有的選擇都準備好了.

字面意思

允許您創建不同風格的對象,同時避免構造器污染。 當有可能創建幾種不同的對象時很有用。 或者當創建對象時涉及很多步驟。

維基百科

構建器模式是一種對象創建軟件設計模式,其目的是找到一種方案,解決伸縮構造函數反模式。

話雖如此,我還是要補充一點,關於伸縮構造函數反模式是什麼。在某一點上,我們都看到了如下構造函數:

public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
}

您可以看到,構造函數的數量可能會很快失控,並且可能難以理解參數的排列。 如果您以後要添加更多選項,此參數列表可能會不斷增長。 這被稱爲伸縮構造器反模式。

編程例子

理想的選擇是使用Builder模式。 首先我們有我們要創造的英雄

public final class Hero {
  private final Profession profession;
  private final String name;
  private final HairType hairType;
  private final HairColor hairColor;
  private final Armor armor;
  private final Weapon weapon;

  private Hero(Builder builder) {
    this.profession = builder.profession;
    this.name = builder.name;
    this.hairColor = builder.hairColor;
    this.hairType = builder.hairType;
    this.weapon = builder.weapon;
    this.armor = builder.armor;
  }
}

And then we have the builder

  public static class Builder {
    private final Profession profession;
    private final String name;
    private HairType hairType;
    private HairColor hairColor;
    private Armor armor;
    private Weapon weapon;

    public Builder(Profession profession, String name) {
      if (profession == null || name == null) {
        throw new IllegalArgumentException("profession and name can not be null");
      }
      this.profession = profession;
      this.name = name;
    }

    public Builder withHairType(HairType hairType) {
      this.hairType = hairType;
      return this;
    }

    public Builder withHairColor(HairColor hairColor) {
      this.hairColor = hairColor;
      return this;
    }

    public Builder withArmor(Armor armor) {
      this.armor = armor;
      return this;
    }

    public Builder withWeapon(Weapon weapon) {
      this.weapon = weapon;
      return this;
    }

    public Hero build() {
      return new Hero(this);
    }
  }

然後它可以用作:

Hero mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();

適用範圍


使用Builder模式

  • 用於創建複雜對象的算法應該獨立於組成對象的部分以及它們是如何被組裝
  • 構造過程必須允許構建的對象的不同表現

真實例子


權威信息

11.業務委託


標題 類別 難度
業務委託(Business Delegate) 業務層 Java,中等難度

意圖


業務委託模式在表示層和業務層之間添加一個抽象層。 通過使用這種設計模式,
我們獲得了層之間的鬆散耦合,
介於層次之間並封裝有關如何定位,連接到,
並與構成應用程序的業務對象進行交互。

這裏寫圖片描述

適用範圍


何時使用業務委託模式

  • 您希望表示和業務層之間鬆散耦合
  • 您想協調多個業務服務的呼叫
  • 你想封裝服務查詢和服務調用

權威信息


12.緩存模式


標題 類別 難度
緩存模式(caching design) 其他 Java,中等難度,性能相關

意圖


通過不立即釋放資源的方式避免昂貴的資源再獲取操作。 資源保留他們的身份,保留在一些快速訪問存儲,並重新使用,以避免再次獲取它們。
這裏寫圖片描述

適用範圍


何時使用緩存模式

  • 重複的資源獲取,初始化和釋放會導致不必要的性能開銷。

權威信息


13.回調模式


標題 類別 難度
回調模式(callback) 其他 Java,簡單難度,函數式編程

意圖


回調是作爲一段可傳遞的執行代碼,是其他代碼的參數。在合適的時間點執行。

這裏寫圖片描述

適用範圍


何時使用回調模式時

  • 在執行某些已定義的活動之後,必須執行同步或異步操作。

真實例子


  • CyclicBarrier constructor can accept callback that will be triggered every time when barrier is tripped.

14.責任鏈模式


標題 類別 難度
責任鏈模式(chain design) 行爲型 Java,GOF,中等難度

意圖


通過給與一個或更多對象處理請求的機會避免將請求的發送者與接收者耦合。責任鏈接收
對象,並沿着鏈傳遞請求,直到有對象處理它爲止。

解釋


真實例子

獸王向他的軍隊發出明確的命令。 最接近的人是指揮官,然後是軍官,然後是士兵。 這裏的指揮官,軍官和士兵組成了一連串的責任。

字面意思

它有助於建立一個對象鏈。 請求從一端進入,並保持前進從對象到對象,直到找到合適的處理程序。

維基描述

在面向對象設計中,責任鏈模式是由命令對象源和一系列處理對象組成的設計模式。 每個處理對象包含定義可以處理的命令對象的類型的邏輯; 其餘的傳遞給鏈中的下一個處理對象。

編程例子

用上面的獸人轉換成我們的例子。 首先我們有Request

public class Request {

  private final RequestType requestType;
  private final String requestDescription;
  private boolean handled;

  public Request(final RequestType requestType, final String requestDescription) {
    this.requestType = Objects.requireNonNull(requestType);
    this.requestDescription = Objects.requireNonNull(requestDescription);
  }

  public String getRequestDescription() { return requestDescription; }

  public RequestType getRequestType() { return requestType; }

  public void markHandled() { this.handled = true; }

  public boolean isHandled() { return this.handled; }

  @Override
  public String toString() { return getRequestDescription(); }
}

public enum RequestType {
  DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}

然後RequestHandler的層次結構

public abstract class RequestHandler {
  private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
  private RequestHandler next;

  public RequestHandler(RequestHandler next) {
    this.next = next;
  }

  public void handleRequest(Request req) {
    if (next != null) {
      next.handleRequest(req);
    }
  }

  protected void printHandling(Request req) {
    LOGGER.info("{} handling request \"{}\"", this, req);
  }

  @Override
  public abstract String toString();
}

public class OrcCommander extends RequestHandler {
  public OrcCommander(RequestHandler handler) {
    super(handler);
  }

  @Override
  public void handleRequest(Request req) {
    if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
      printHandling(req);
      req.markHandled();
    } else {
      super.handleRequest(req);
    }
  }

  @Override
  public String toString() {
    return "獸人指揮官";
  }
}

// OrcOfficer and OrcSoldier are defined similarly as OrcCommander

然後我們有獸人國王,他命令並形成了鏈

public class OrcKing {
  RequestHandler chain;

  public OrcKing() {
    buildChain();
  }

  private void buildChain() {
    chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
  }

  public void makeRequest(Request req) {
    chain.handleRequest(req);
  }
}

然後使用如下

OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle"
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner"
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax"

適用範圍


何時使用責任鏈

  • 多個對象可以處理請求,並且處理程序不是先驗知道的。 處理程序應自動確定。
  • 您要向幾個對象之一發出請求,而不明確指定接收者。
  • 可以動態指定可處理請求的對象集。

真實例子


權威信息


15.命令模式


標題 類別 難度
命令模式(command design) 行爲型 Java,GOF,中等難度,函數式編程

意圖


將請求封裝爲一個對象,從而使你用不同的請求對客戶進行參數化,對請求排隊或記錄請求日誌,以及
支持可撤銷的操作。

這裏寫圖片描述

適用範圍


當你想要使用命令模式

  • 通過執行操作來參數化對象。您可以用具有回調函數的過程語言表達這種參數化,即在稍後註冊的地方註冊的函數。命令是一個面向對象的方式替換回調。
  • 在不同時間指定,隊列和執行請求。 Command對象可以具有與原始請求無關的生命週期。如果請求的接收方可以以地址空間無關的方式表示,那麼您可以將請求的命令對象傳送到不同的進程,並在此處執行請求。
  • 支持撤消。命令的執行操作可以存儲在命令本身中將其效果反轉的狀態。 Command界面必須有一個添加的“非執行”操作,可以將先前調用的效果反轉爲執行。執行的命令存儲在歷史記錄列表中。無限級別的撤消和重做是通過遍歷該列表來分別來回執行並且執行的
  • 支持記錄更改,以便在系統崩潰的情況下重新應用它們。通過使用加載和存儲操作增加Command界面,可以保留持久性的更改日誌。從崩潰中恢復包括從磁盤重新加載記錄的命令,並使用執行操作重新執行它們
  • 構建基於原始操作的高級操作的系統。 這種結構在支持交易的信息系統中是常見的。 事務封裝了一組數據更改。 Command模式提供了一種模型交易的方式。 命令有一個通用的界面,讓你以相同的方式調用所有的事務。 該模式還可以通過新的事務來擴展系統

典型用例


  • 保留歷史請求
  • 實現回調功能
  • 實現撤消功能

真實例子


權威信息


16.組合模式


標題 類別 難度
組合模式(Composite design) 結構型 Java,GOF,中等難度

意圖


將對象組合成樹結構以表示部分整體層次結構。 組合允許客戶端處理單個對象和組合的對象一致。

解釋


真實例子

每個句子都是由字符組成的單詞組成。 這些對象中的每一個都是可打印的,它們可以在它們之前或之後打印出一些東西,因爲句子總是以完全停止的方式結束,而且在它之前總是有空格

字面意思

組合模式使客戶能夠以統一的方式對待各個對象。

維基百科

在軟件工程中,組合模式是分區設計模式。 組合模式描述了一組對象的處理方式與對象的單個實例相同。 組合的意圖是將對象“組合”成樹結構以表示部分整體層次結構。 實現組合模式使客戶能夠一致地處理單個對象和對象組。

編程例子

以我們上面的例子爲例。在這裏,我們有不同類型的基類,可打印的字符

public abstract class LetterComposite {
  private List<LetterComposite> children = new ArrayList<>();
  public void add(LetterComposite letter) {
    children.add(letter);
  }
  public int count() {
    return children.size();
  }
  protected void printThisBefore() {}
  protected void printThisAfter() {}
  public void print() {
    printThisBefore();
    for (LetterComposite letter : children) {
      letter.print();
    }
    printThisAfter();
  }
}

public class Letter extends LetterComposite {
  private char c;
  public Letter(char c) {
    this.c = c;
  }
  @Override
  protected void printThisBefore() {
    System.out.print(c);
  }
}

public class Word extends LetterComposite {
  public Word(List<Letter> letters) {
    for (Letter l : letters) {
      this.add(l);
    }
  }
  @Override
  protected void printThisBefore() {
    System.out.print(" ");
  }
}

public class Sentence extends LetterComposite {
  public Sentence(List<Word> words) {
    for (Word w : words) {
      this.add(w);
    }
  }
  @Override
  protected void printThisAfter() {
    System.out.print(".");
  }
}

Then we have a messenger to carry messages

public class Messenger {
  LetterComposite messageFromOrcs() {
    List<Word> words = new ArrayList<>();
    words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
    words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
    words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
    words.add(new Word(Arrays.asList(new Letter('a'))));
    words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter('p'))));
    words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
    words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
    words.add(new Word(Arrays.asList(new Letter('a'))));
    words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y'))));
    return new Sentence(words);
  }

  LetterComposite messageFromElves() {
    List<Word> words = new ArrayList<>();
    words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter('h'))));
    words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter('d'))));
    words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter('r'), new Letter('s'))));
    words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter('m'))));
    words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter('r'))));
    words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter('t'), new Letter('h'))));
    return new Sentence(words);
  }
}

然後它可以用作

LetterComposite orcMessage = new Messenger().messageFromOrcs();
orcMessage.print(); // Where there is a whip there is a way.
LetterComposite elfMessage = new Messenger().messageFromElves();
elfMessage.print(); // Much wind pours from your mouth.

適用範圍


使用組合模式時

  • 您想要表示對象的部分-整體層次結構
  • 您希望客戶能夠忽略對象整體和單個對象的組合之間的差異。 客戶將統一處理複合結構中的所有對象

真實例子


權威信息


17.轉換器模式


標題 類別 難度
轉換器(convert) 簡單難度

意圖


轉換器模式的目的是提供一種通用的,在相應類型之間進行雙向轉換,類型轉換不需要了解彼此,是一種乾淨的轉換方式。 此外,轉換器模式引入了雙向收集映射,將樣板代碼減少到最小。

這裏寫圖片描述

適用範圍


在以下情況下使用轉換器模式:

  • 當您有類型邏輯對應哪個其他,您需要在它們之間轉換實體
  • 當您想根據上下文提供不同的類型轉換方式時
  • 每當您引入DTO(數據傳輸對象)時,您可能需要將其轉換爲具體領域對象

權威信息


18.命令查詢的責任分離模式


標題 類別 難度
命令查詢的責任分離(CQRS design) 結構型 中等難度

意圖


CQRS命令查詢責任分離 - 將查詢與命令端分開(博主理解:有點像數據庫的讀寫分離,但對寫一端有了歷史記錄變化的追溯,) 。
這裏寫圖片描述

適用範圍


使用CQRS模式

  • 您要獨立擴展查詢和命令。
  • 您希望對查詢和命令使用不同的數據模型。 在處理複雜域時很有用。
  • 您想使用基於事件源的UI架構。

權威信息


19.數據訪問對象模式


標題 類別 難度
數據訪問對象模式(dao design) 持久層 初始難度

意圖


對象提供了某種類型的數據庫的抽象接口或其他持久機制。
這裏寫圖片描述

適用範圍


在以下任何情況下使用數據訪問對象

  • 當您想要整合數據層的訪問方式
  • 當你想避免編寫多個數據檢索/持久層

權威信息


20.數據總線模式


標題 類別 難度
.數據總線模式(Data Bus design) 結構型 中等難度

意圖


在不需要應用組件彼此瞭解的情況下,發送消息/事件。 他們只需要知道關於要發送的消息/事件的類型。

這裏寫圖片描述

適用訪問


使用數據總線模式

  • 您希望您的組件自己決定要接收哪些消息/事件
  • 你想有多對多的交互
  • 您希望您的組件彼此不瞭解

相關模式


與數據總線相似

  • 調停員模式與數據總線成員自己決定是否要接受任何給定的消息
  • 觀察者模式,支持多對多通信
  • 發佈/訂閱模式與數據總線模式相比,解耦發佈者和訂閱者

21.數據映射模式


標題 類別 難度
數據映射模式(data-mapper design) 結構型 簡單難度

意圖


用於在對象和數據庫之間移動數據,同時保持它們彼此獨立。

這裏寫圖片描述

適用範圍


在以下任何情況下使用數據映射器

  • 當您要將數據對象與數據庫訪問層分離時
  • 當你想編寫多個數據檢索/持久化實現

權威信息


22.數據傳輸對象模式


標題 類別 難度
數據傳輸對象模式(data-transfer-object design) 結構型 簡單難度

意圖


從客戶端到服務器一次性傳遞具有多個屬性的數據,
以避免多次呼叫到遠程服務器。

這裏寫圖片描述

適用範圍


使用數據傳輸對象模式

  • 客戶端要求多個信息。 信息是相關的。
  • 當您想提高性能以獲取資源時。
  • 您希望減少遠程呼叫的數量。

權威信息


23.裝飾模式


標題 類別 難度
裝飾模式(Decorator design) 結構型 簡單難度

衆所周知


Wrapper

意圖


給對象動態附加額外的責任,裝飾模式爲擴展子類提供了靈活的替代方案。

解釋


真實世界

生活在附近山丘上的一個憤怒的巨魔。 平時它是空手的,但有時它有武器。 爲了武裝巨魔,沒有必要創建一個新的巨魔,而是用合適的武器動態地裝飾它。

字面意思

裝飾模式使您可以在運行時動態地更改對象的行爲,將其包裝在裝飾器類的對象中。

維基解釋

在面向對象編程中,裝飾器模式是一種設計模式,允許靜態或動態地將行爲添加到單個對象,而不會影響同一類中其他對象的行爲。 裝飾師模式對於遵守單一責任原則通常是有用的,因爲它允許在具有獨特關注領域的類別之間劃分功能。

編程例子

讓我們看看巨魔的例子。 首先我們有一個簡單的巨魔實現巨魔接口

public interface Troll {
  void attack();
  int getAttackPower();
  void fleeBattle();
}

public class SimpleTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class);

  @Override
  public void attack() {
    LOGGER.info("The troll tries to grab you!");
  }

  @Override
  public int getAttackPower() {
    return 10;
  }

  @Override
  public void fleeBattle() {
    LOGGER.info("The troll shrieks in horror and runs away!");
  }
}

接下來我們要爲巨魔添加棒子。 我們可以通過使用裝飾器來動態地執行

public class ClubbedTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);

  private Troll decorated;

  public ClubbedTroll(Troll decorated) {
    this.decorated = decorated;
  }

  @Override
  public void attack() {
    decorated.attack();
    LOGGER.info("The troll swings at you with a club!");
  }

  @Override
  public int getAttackPower() {
    return decorated.getAttackPower() + 10;
  }

  @Override
  public void fleeBattle() {
    decorated.fleeBattle();
  }
}

這是巨魔的行爲

// simple troll
Troll troll = new SimpleTroll();
troll.attack(); //巨魔試圖抓住你!
troll.fleeBattle(); // 巨魔逃跑了!

//通過添加裝飾器來改變簡單巨魔的行爲
troll = new ClubbedTroll(troll);
troll.attack(); //巨魔試圖抓住你! 巨魔用棍子敲你頭!
troll.fleeBattle(); //巨魔逃跑了!

適用範圍


使用裝飾模式如下

  • 動態,透明地爲各個對象添加責任,即不影響其他對象
  • 可撤銷的責任
  • 通過子類化擴展是不切實際的。 有時,大量的獨立擴展是可能的,並且會產生一個子類的爆炸,裝飾模式支持任意組合。 或者類定義可能被隱藏或以其他方式不可用於子類化

真實世界


權威信息


24.委託模式


標題 類別 難度
委託模式(delegation design) 行爲型 簡單難度

衆所周知


代理模式(Proxy Pattern)

意圖


這是一種技術,其中一個對象表現某種行爲給外部,但是該行爲的實現委託給相關聯的對象。(博主描述,比如你一個對象要調用一個方法,但是你不直接操作這個對象本身,而是操作和他關聯的一個其他對象,並且該對象實現了那個方法的接口)

這裏寫圖片描述

適用範圍


爲了實現以下目的,使用委託模式

  • 減少方法與類的耦合
  • 組件的行爲相同,但意識到這種情況在將來會發生變化。

權威信息


25.依賴注入模式


標題 類別 難度
依賴注入模式(Dependency Injection) 行爲型 簡單難度

意圖


依賴注入是一種軟件設計模式,其中一個或更多的依賴(或服務)被注入或通過引用傳遞到依賴對象(或客戶端)並作爲客戶端狀態的一部分。該模式將客戶機的依賴關係的創建與其自身的分離行爲,允許程序設計鬆散耦合並遵循
控制反轉和單一責任原則。

alt text

適用範圍


何時使用依賴注入模式

  • 當您需要從對象中刪除具體實現的知識時
  • 使用模擬對象或存根單獨測試類的單元測試

26.雙重鎖模式


標題 類別 難度
雙重鎖模式(Double Checked Locking design) 併發型 簡單難度,原子性

意圖


通過先判斷是否需要獲取鎖來減少鎖獲取的開銷,如果需要執行鎖,在進行實際的鎖操作。在鎖定之後再判斷一次需要獲取鎖,若不需要擇表明不需要執行邏輯,加快鎖的釋放。
這裏寫圖片描述

適用範圍


何時使用雙重檢查鎖定模式

  • 在對象創建中存在併發訪問,例如 單例,您想要創建同一個類的單個實例,並檢查是否爲空,或者當有兩個或多個線程檢查實例是否爲空時,可能還不夠。
  • 方法的行爲根據一些約束而改變,並且這些約束在此方法中改變,就有一個併發訪問。

27.雙重派對模式


標題 類別 難度
雙重派對模式(Double Dispatch design) 其他 中等難度,原子性

意圖


雙重分發模式創建基於接收器的可維護的動態行爲和參數類型。

這裏寫圖片描述

適用範圍

何時使用雙重派對模式

  • 動態行爲不僅僅基於接收對象的類型而是基於接收方法的參數類型來定義。

真實例子


28. 執行程序流監測器


標題 類別 難度
執行程序流監測器(EIP WireTap design) 企業集成 中等難度

意圖


在大多數集成案例中,需要監控流經系統的消息。 通常實現通過攔截消息並將其重定向到不同的位置,如控制檯,文件系統或數據庫。重要的是,這種功能不應該修改原始消息並影響處理路徑。

這裏寫圖片描述

適用範圍


使用執行程序流監測器

  • 您需要監控流經系統的消息
  • 您需要將相同的未更改的消息重定向到兩個不同的端點/路徑

權威信息


29. 事件聚合器


標題 類別 難度
事件聚合器(event-aggregator design) 結構型 簡單難度,響應式

意圖


一個具有很多對象的系統會導致複雜性,客戶端希望訂閱活動。
客戶端必須找到並註冊每個對象分別,如果每個對象有多個事件,那麼每個事件需要單獨訂閱。 事件聚合器作爲事件的單一來源, 它註冊了許多對象的所有事件允許客戶端僅使用聚合器註冊。

這裏寫圖片描述

適用範圍


何時使用事件聚合器模式

  • 當你有很多的潛在的事件來源(對象), 但不想讓觀察者處理註冊,您可以將註冊邏輯集中到事件聚集器。 除了簡化註冊,事件聚合器簡化了使用觀察者的內存管理問題。

權威信息


30. 事件異步


標題 類別 難度
事件異步(Event-based Asynchronous design) 併發 中等難度,性能

意圖


基於事件的異步模式提供了多線程應用程序的優勢,同時隱藏了許多應用程序
的多線程設計中固有的複雜問題。 使用支持此模式的類可以允許您:

  • 在後臺執行耗時的任務,例如下載和數據庫操作,而不會中斷您的應用程序。
  • 同時執行多個操作,每次完成時接收通知。
  • 等待資源可用,而不停止(“懸掛”)您的應用程序。
  • 使用熟悉的事件和委託模型與掛起的異步操作進行通信。

這裏寫圖片描述

適用範圍


使用基於事件的異步模式

  • 需要耗費時間的任務,並且運行在後臺不會中斷當前的應用程序。

權威信息


31. 基於事件驅動的體系結構


標題 類別 難度
基於事件驅動的體系結構(Event Driven Architecture design) 結構型 困難,響應式

意圖


發送或通知對象狀態的變化,給使用了基於事件驅動的體系結構其他應用。

適用範圍


這裏寫圖片描述
使用事件驅動的架構

  • 你想創建一個鬆散耦合的系統
  • 你想建立一個更加敏感的系統
  • 你想要一個更容易擴展的系統

真實例子


權威信息


32. 事件隊列


標題 類別 難度
事件隊列(event-queue) 併發 簡單難度,隊列

意圖


事件隊列是一個很好的模式,如果你有一個有限的可訪問資源(例如:音頻或數據庫),但您需要處理所有要使用的請求。它將所有請求放入隊列中並異步處理。當事件完成時,從隊列中移除,並給下一個事件提供資源。

這裏寫圖片描述

適用範圍


使用事件隊列模式

  • 異步隊列接受未知的請求,訪問有限的資源

權威信息


33. 事件捕捉


標題 類別 難度
事件捕捉(event sourcing design) 結構型 困難難度,性能

意圖


不要僅將數據的當前狀態存儲在域中,而是使用追加專用存儲來記錄對該數據執行的所有操作。貯存系統可以讓領域對象物質化(在json文件中保存對對象的操作)。 這可以簡化複雜域中的任務,避免在同步數據模型和業務領域的同時,同時提高性能,可擴展性和響應能力。 它還可以爲事務數據提供一致性,並保持完整的審計跟蹤和歷史記錄,從而可以實現補償操作。
這裏寫圖片描述

適用範圍


何時使用事件捕捉模式

  • 即使您的應用程序狀態具有複雜的關係數據結構,您仍然需要保持應用程序狀態的高性能
  • 您需要記錄您的應用程序狀態和恢復任何時間的狀態的能力。
  • 您需要通過重播過去的事件來調試生產問題。

真實世界


權威信息


34. 執行環繞


標題 類別 難度
執行環繞(execute around design) 其他 簡單難度,術語

意圖


執行圍繞術語的意思是釋放用戶的某些動作,應始終在業務方法之前和之後執行。 一個很好的例子
這是資源分配和釋放,讓用戶只需要關係如何操作資源。

這裏寫圖片描述

適用範圍


何時使用執行環繞

  • 你使用一個API, 同時需要調用別的方法在周圍。

權威信息


35. 擴展對象


標題 類別 難度
擴展對象( Extension objects) 行爲型 中等難度

意圖


預計在將來需要擴展對象的接口。 額外接口由擴展對象定義。

這裏寫圖片描述

適用範圍


在以下情況下使用擴展對象模式

  • 您需要支持向現有類添加新的或不可預見的接口,並且不希望影響不需要此新界面的客戶端。 擴展對象允許您通過在單獨的類中定義相關操作來保持相關操作
  • 代表關鍵抽象的類對於不同的客戶端起着不同的作用。 課程的角色數量應該是開放的。 需要保留關鍵抽象本身。 例如,客戶對象仍然是客戶對象,即使不同的子系統以不同的方式查看它。
  • 一個類的新行爲是可擴展的,而不是將它進行子類化。

真實世界


36. 外觀模式


標題 類別 難度
外觀模式( facade design)) 結構型 簡單難度,GOF

意圖


爲子系統中的一組接口提供統一的接口。
外觀模式定義了一個更高級別的界面,使子系統更易於使用。

解釋


現實例子

金礦如何運作? “礦工們下去挖金了!” 你說。 這就是你所相信的,因爲你正在使用一個簡單的界面,goldmine在外面提供,在內部它必須做很多事情才能實現。 複雜子系統的這個簡單界面是一個外觀。

字面意思

外觀模式爲複雜子系統提供簡化的界面。

維基解釋

外觀是一個對象,它提供了一個更簡單的界面,用於更大量的代碼,例如類庫。

編程例子

從上面我們的金礦做例子。 在這裏,我們有矮人的礦工級別

//矮人工人
public abstract class DwarvenMineWorker {

  private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class);

  public void goToSleep() {
    LOGGER.info("{} goes to sleep.", name());
  }

  public void wakeUp() {
    LOGGER.info("{} wakes up.", name());
  }

  public void goHome() {
    LOGGER.info("{} goes home.", name());
  }

  public void goToMine() {
    LOGGER.info("{} goes to the mine.", name());
  }

  private void action(Action action) {
    switch (action) {
      case GO_TO_SLEEP:
        goToSleep();
        break;
      case WAKE_UP:
        wakeUp();
        break;
      case GO_HOME:
        goHome();
        break;
      case GO_TO_MINE:
        goToMine();
        break;
      case WORK:
        work();
        break;
      default:
        LOGGER.info("Undefined action");
        break;
    }
  }

  public void action(Action... actions) {
    for (Action action : actions) {
      action(action);
    }
  }

  public abstract void work();

  public abstract String name();

  static enum Action {
    GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
  }
}

//矮人隧道挖掘機操作者
public class DwarvenTunnelDigger extends DwarvenMineWorker {

  private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class);

  @Override
  public void work() {
    LOGGER.info("{} creates another promising tunnel.", name());
  }

  @Override
  public String name() {
    return "Dwarven tunnel digger";
  }
}

//矮人黃金挖掘機操作者
public class DwarvenGoldDigger extends DwarvenMineWorker {

  private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class);

  @Override
  public void work() {
    LOGGER.info("{} digs for gold.", name());
  }

  @Override
  public String name() {
    return "Dwarf gold digger";
  }
}

//矮人礦車操作者
public class DwarvenCartOperator extends DwarvenMineWorker {

  private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class);

  @Override
  public void work() {
    LOGGER.info("{} moves gold chunks out of the mine.", name());
  }

  @Override
  public String name() {
    return "Dwarf cart operator";
  }
}

爲了操作所有這些金礦工人,我們有一個外觀

public class DwarvenGoldmineFacade {

  private final List<DwarvenMineWorker> workers;

  public DwarvenGoldmineFacade() {
    workers = new ArrayList<>();
    workers.add(new DwarvenGoldDigger());
    workers.add(new DwarvenCartOperator());
    workers.add(new DwarvenTunnelDigger());
  }

  public void startNewDay() {
    makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
  }

  public void digOutGold() {
    makeActions(workers, DwarvenMineWorker.Action.WORK);
  }

  public void endDay() {
    makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
  }

  private static void makeActions(Collection<DwarvenMineWorker> workers,
      DwarvenMineWorker.Action... actions) {
    for (DwarvenMineWorker worker : workers) {
      worker.action(actions);
    }
  }
}

現在使用這個外觀

DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();
facade.startNewDay();
// Dwarf gold digger wakes up.
// Dwarf gold digger goes to the mine.
// Dwarf cart operator wakes up.
// Dwarf cart operator goes to the mine.
// Dwarven tunnel digger wakes up.
// Dwarven tunnel digger goes to the mine.
facade.digOutGold();
// Dwarf gold digger digs for gold.
// Dwarf cart operator moves gold chunks out of the mine.
// Dwarven tunnel digger creates another promising tunnel.
facade.endDay();
// Dwarf gold digger goes home.
// Dwarf gold digger goes to sleep.
// Dwarf cart operator goes home.
// Dwarf cart operator goes to sleep.
// Dwarven tunnel digger goes home.
// Dwarven tunnel digger goes to sleep.

適用範圍


何時使用外觀模式

  • 您想爲複雜子系統提供簡單的界面。子系統在發展時通常會變得更加複雜。大多數模式在應用時會導致越來越多的類。這使得子系統更可重用並且更容易定製,但是對於不需要自定義的客戶端也變得更難使用。外觀可以提供對於大多數客戶端來說足夠好的子系統簡單默認視圖。只有需要更多可定製性的客戶才需要越過外觀模式。
  • 客戶端和抽象的實現類之間存在很多依賴關係。引入一個外觀來將子系統與客戶端和其他子系統分離,從而提高子系統的獨立性和可移植性。
    *你 想分層你的子系統。使用門面來定義每個子系統級別的入口點。如果子系統是依賴的,那麼您可以通過使它們僅通過其外觀立面相互通信來簡化它們之間的依賴關係

權威信息


37. 工廠套件


標題 類別 難度
工廠套件( Factory Kit design) 創建型 簡單難度,函數式

意圖


使用分離的構建器和工廠接口定義一個內容不可變化的工廠。

這裏寫圖片描述

適用範圍


何時使用工廠套件模式

  • 一個類不能預料到它必須創建的對象類
  • 您只需要一個新的自定義構建器實例,而不是全局實例,你明確地想要定義工廠可以建立的對象類型
  • 你想要一個分離的構建器和創建者界面

權威信息


38. 工廠方法


標題 類別 難度
工廠方法( Factory Method design) 創建型 簡單難度,GOF

衆所周知


Virtual Constructor

意圖


定義一個用於創建一個對象的接口,但是讓子類決定要實例化哪個類。 工廠方法允許類推遲
實例化到子類。

解釋


真實世界

鐵匠製造武器。 精靈需要精靈武器和獸人需要獸醫武器。 根據手頭的客戶,正確的類型被鐵匠製造。

字面意思

它提供了一種將實例化邏輯委託給子類的方法。

維基解釋

在基於類的編程中,工廠方法模式是使用工廠方法來處理創建對象的問題的創建模式,而不必指定將要創建的對象的確切類。 這可以通過調用工廠方法(在接口中指定並由子類實現)來實現,或者在基類中實現,並且可選地被派生類覆蓋,而不是調用構造函數。

編程例子

以我們的鐵匠爲例。 首先我們有一個鐵匠接口和一些實現

public interface Blacksmith {
  Weapon manufactureWeapon(WeaponType weaponType);
}

public class ElfBlacksmith implements Blacksmith {
  public Weapon manufactureWeapon(WeaponType weaponType) {
    return new ElfWeapon(weaponType);
  }
}

public class OrcBlacksmith implements Blacksmith {
  public Weapon manufactureWeapon(WeaponType weaponType) {
    return new OrcWeapon(weaponType);
  }
}

現在,由於客戶來了,鐵匠被要求製造符合客戶武器類型的武器

Blacksmith blacksmith = new ElfBlacksmith();
blacksmith.manufactureWeapon(WeaponType.SPEAR);
blacksmith.manufactureWeapon(WeaponType.AXE);
// 創建精靈武器

適用範圍

何時使用“工廠方法”模式

  • 一個類不能預料到它必須創建的對象類
  • 一個類希望其子類來指定它創建的對象
  • 類將責任委託給幾個輔助子類之一,並且您想要本地化代理的哪個助手子類的知識

真實例子

權威信息

39. 功能切換


標題 類別 難度
功能切換(Feature Toggle design) 行爲型 簡單難度

衆所周知


Feature Flag

意圖


根據屬性或分組切換代碼執行路徑。 允許新功能被髮布,測試並推出。 如果需要,可以快速切換回舊功能。 應該指出的是,這種模式,
可以輕鬆引入代碼複雜性。 還有一個令人擔憂的是,切換最終的舊功能
逐步淘汰不會被刪除,從而導致冗餘代碼的氣味和維護成本提高。
這裏寫圖片描述

適用範圍


使用功能切換模式

  • 爲不同用戶提供不同的功能。
  • 逐步推出新功能。
  • 在開發和生產環境之間切換。

權威信息


40. 流暢的接口


標題 類別 難度
流暢的接口(Fluent Interface design) 其他 中等難度,函數式

意圖


流暢的接口提供了一個易於閱讀,流暢的接口,通常會模仿一個領域特定的語言。 使用這種模式可以生成像人類語言一樣可供閱讀的代碼

實現


流暢的接口可以使用如下方式來實現

  • 方法鏈 - 調用方法返回一些可以調用其他方法的對象。
  • 靜態工廠方法和導入
  • 命名參數 - 可以使用靜態工廠方法在Java中進行模擬。
    這裏寫圖片描述

適用範圍


使用流暢的界面模式

  • 您提供的API將受益於類似DSL(領域特定語言)的用途
  • 您有難以配置或使用的對象

真實例子


權威信息


41. 流量模式


標題 類別 難度
流量模式(flux design) 持久層 中等難度

意圖


Flux避開MVC設計模式,有利於單向數據流。 當一個
用戶與視圖進行交互,視圖通過中央傳播動作調度員,到持有應用程序的數據和業務的各種商業邏輯,更新所有受影響的視圖。

博主思考
一個客戶端程序如Adnroid程序,定義用戶觸發的事件, 通過Stroe讓模型和視圖分離,這在單事件觸發單頁面的時候體會不出好處,反而會增加定義事件Action的負擔,但是單A頁面觸發的事件,引起多個B,C,View 視圖的UI變化時,便能很清晰的追溯整個事件流的過程。
這裏寫圖片描述

適用範圍


使用流量模式時

  • 您希望專注於爲應用程序的數據創建顯式的和可理解的更新路徑,這使得在開發過程中跟蹤更改變得更簡單,並使bug更容易跟蹤和修復。

權威信息


42. 享元模式


標題 類別 難度
享元模式(flyweight design) 持久層 GOF,中等難度,性能

意圖


使用共享技術有效支持大量細粒度的對象

例子


真實世界

煉金術士的商店貨架上擺滿了魔法藥水。很多藥水都是一樣的,所以不需要爲它們創建新的對象。相反,一個對象實例可以代表多個貨架項目,因此內存佔用仍然很小。

字面意思

它通過與相似對象共享儘可能減少內存使用或計算開銷。

維基描述

在計算機編程中,享元模式是一種軟件設計模式。 享元模式通過與其他類似對象儘可能多的共享數據來最小化內存使用的對象; 當簡單的重複表示將使用不可接受的內存量時,它是大量使用對象的一種方式。

編程例子

從上面翻譯我們的煉金術士商店示例。 首先我們有不同的藥水類型

//藥劑
public interface Potion {
  void drink();
}

//回覆藥劑(補血?)
public class HealingPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(HealingPotion.class);
  @Override
  public void drink() {
    LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
  }
}

//升水藥劑(補藍?)
public class HolyWaterPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(HolyWaterPotion.class);
  @Override
  public void drink() {
    LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
  }
}

//隱形藥劑
public class InvisibilityPotion implements Potion {
  private static final Logger LOGGER = LoggerFactory.getLogger(InvisibilityPotion.class);
  @Override
  public void drink() {
    LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this));
  }
}

然後是實際的享元對象,它是用於創建藥水的工廠

public class PotionFactory {

  private final Map<PotionType, Potion> potions;

  public PotionFactory() {
    potions = new EnumMap<>(PotionType.class);
  }

  Potion createPotion(PotionType type) {
    Potion potion = potions.get(type);
    if (potion == null) {
      switch (type) {
        case HEALING:
          potion = new HealingPotion();
          potions.put(type, potion);
          break;
        case HOLY_WATER:
          potion = new HolyWaterPotion();
          potions.put(type, potion);
          break;
        case INVISIBILITY:
          potion = new InvisibilityPotion();
          potions.put(type, potion);
          break;
        default:
          break;
      }
    }
    return potion;
  }
}

如下使用

PotionFactory factory = new PotionFactory();
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)

適用範圍


享元模式的有效性在很大程度上取決於如何使用和它在哪裏使用。 當以下所有的時候應用享元模式
真正

  • 應用程序使用大量的對象
  • 存儲成本高,因爲物體數量龐大
  • 一旦外部狀態被移除,許多對象組可能被相對較少的共享對象所取代
  • 應用程序不依賴於對象標識。 由於可以共享享元對象,所以對於概念上不同的對象,身份測試將返回true。

真實世界


權威信息


43. 前臺控制


標題 類別 難度
前臺控制(front controller design) 持久層 中等難度

意圖


爲網站的所有請求引入共同的處理器。 我們可以封裝常見的功能,如安全性,
國際化,路由和記錄在一個單一的地方。

這裏寫圖片描述

適用範圍


使用前端控制器模式

  • 您希望將通用請求處理功能封裝在一個單一的位置
  • 你想實現動態請求處理,即改變路由而不修改代碼
  • 使web服務器配置可移植,您只需要註冊處理程序Web服務器的具體方式

真實例子


權威信息


44. 被監視的掛起


標題 類別 難度
被監視的掛起(Guarded Suspension design) 併發 簡單難度

意圖


當想要對處於不在合適狀態的對象執行方法時,可以適用被監視的掛起來處理一些情況

這裏寫圖片描述

適用範圍


當開發人員知道方法執行將在一段有限的時間內被阻止時,使用Guarded Suspension模式

關聯模式


  • Balking

45. 半同步半異步


標題 類別 難度
半同步半異步(Half-Sync/Half-Async design) 併發 中等難度

意圖


半同步/半異步模式,從異步中分離出同步的I/O,簡化並行編程工作,沒有降低執行效率。

這裏寫圖片描述

適用範圍


使用半同步/半異步模式

  • 系統具有以下特點:
    • 系統必須執行任務以響應異步發生的外部事件,如OS中的硬件中斷
    • 專用獨立的控制線程爲每個外部事件源執行同步I / O是沒有效率的
    • 如果同步執行I / O,系統中較高級別的任務可以大大簡化。
  • 系統中的一個或多個任務必須運行在單個控制線程中,而其他任務可能受益於多線程。

真實世界


權威信息


46. 六角結構,又名端口&適配器結構


標題 類別 難度
六角結構,又名端口&適配器結構(Hexagonal Architecture) 併發 專家難度

衆所周知


  • Ports and Adapters
  • Clean Architecture
  • Onion Architecture

意圖


不同的用戶可以相似的驅動應用程序,自動化測試或批處理腳本驅動,並與其最終的運行時設備和數據庫隔離開發和測試。
這裏寫圖片描述

適用範圍


使用六角架構模式

  • 當應用程序需要獨立於任何框架時
  • 重要的是應用程序高度可維護和完全可測試

教程


演講


真實世界


  • Apache Isis builds generic UI and REST API directly from the underlying domain objects

權威信息


47. 攔截過濾


標題 類別 難度
攔截過濾(Intercepting Filter) 行爲型 中等難度

意圖


提供可插拔過濾器進行必要的預處理和對從客戶端到目標的請求進行後處理
這裏寫圖片描述

適用模式


使用攔截過濾模式

  • 系統使用預處理或後處理請求
  • 系統應對請求進行認證/授權/記錄或跟蹤,然後將請求傳遞給相應的處理程序
  • 您想要一種模塊化的方法來配置預處理和後處理方案

真實世界


權威信息


48. 解釋器模式


標題 類別 難度
解釋器模式(Interpreter) 行爲型 GOF,中等難度

意圖


給定一種語言,根據解釋器定義其語法的表示。

這裏寫圖片描述

適用範圍


當有語言時使用解釋器模式解釋,你可以用解釋器表示語言中的語句作爲抽象語法
樹木。 解釋者模式如下最有效

  • 語法簡單。 對於複雜的語法,語法的類層次結構變得很大,難以管理。 在這種情況下,解析器發生器等工具是更好的選擇。 他們可以解釋表達式而不構建抽象語法樹,這樣可以節省空間和可能的時間
    效率並不是關鍵。 最有效的口譯員通常不是通過直接解釋解析樹來實現的,而是首先將它們轉換成另一種形式。 例如,正則表達式通常被轉換成狀態機。 但是即使如此,翻譯者也可以通過解釋器模式實現,因此模式仍然適用

真實世界


權威信息


49. 迭代器模式


標題 類別 難度
迭代器模式(iterator) 行爲型 GOF,簡單難度

衆所周知


Cursor

意圖


提供順序訪問聚合對象元素的方法,並且不暴露其底層表現。
這裏寫圖片描述

適用範圍


何時使用迭代器模式

  • 訪問聚合對象的內容而不暴露其內部表示
  • 支持聚合對象的多次遍歷
  • 提供用於遍歷不同聚集結構的統一界面

真實例子


權威信息


50. 層模式


標題 類別 難度
層模式(layers) 結構型 Spring,中等難度

意圖


層是一種軟件的體系結構風格。
將應用程序劃分成幾個不同層次。

這裏寫圖片描述

適用範圍


使用層架構時

  • 您希望將軟件職責明確分爲程序的不同部分
  • 您希望防止更改在整個應用程序中傳播
  • 您希望使您的應用程序更加可維護和可測試

權威信息


51. 懶加載

標題 類別 難度
懶加載(Lazy Loading) 其他 術語,簡單難度,性能

意圖


懶加載是一種常見的模式用於延遲初始化一個對象,直到需要的時候。 它可以有助於應用的運作效率適當使用。

這裏寫圖片描述

適用範圍


使用Lazy Loading術語

  • 加載對象是昂貴的,或者根本不需要加載對象

真實例子


  • JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY

權威信息


52. 標記接口

標題 類別 難度
標記接口(Lazy Loading) 設計 簡單難度

意圖


使用空接口作爲標記來區分處理對象。

這裏寫圖片描述

適用範圍


何時使用標記界面圖案

  • 你想從普通對象中識別特殊對象(以不同方式對待它們)
  • 你想標記一些對象可用於某種操作

真實例子


權威信息


53. 中間人模式

標題 類別 難度
中間人模式(mediator) 行爲型 GOF,中等難度

意圖


定義一個對象,用來封裝一組對象交互。
中介者通過保持其他對象的引用來促進鬆耦合,
它可以讓你獨立地改變他們的互動。

這裏寫圖片描述

適用範圍


使用中介模式

  • 一組對象以明確定義但複雜的方式進行通信。 所產生的相互依賴關係是非結構化的,難以理解
  • 重複使用對象是困難的,因爲它涉及並與許多其他對象進行通信
  • 分佈在幾個類之間的行爲應該可以自定義,而不需要很多的子類化

真實例子


權威信息


54. 備忘錄模式

標題 類別 難度
備忘錄模式(Memento) 行爲型 GOF,中等難度

衆所周知


Token

意圖


不侵犯封裝,捕獲和外部化
對象的內部狀態,以便稍後可以將對象恢復到此狀態。

這裏寫圖片描述

適用範圍


當使用備忘者模式時

  • 必須保存對象狀態的快照,以便稍後可以將其恢復到該狀態
  • 獲取狀態的接口將暴露實現細節並破壞對象的封裝

真實世界


權威信息


55.消息渠道

標題 類別 難度
消息渠道(Message Channel) 交互 EIP,Apache Camel™

意圖


當兩個應用程序使用消息系統進行通信時,他們通過使用邏輯地址進行通信
的系統,所謂的消息通道。

這裏寫圖片描述

適用範圍


使用消息通道模式

  • 兩個或多個應用程序需要使用消息傳遞系統進行通信

真實例子


56.模型-視圖-控制器

標題 類別 難度
模型-視圖-控制器(Model-View-Controller) 持久層 中等難度

意圖


將用戶界面分爲三個互連的組件:
模型,視圖和控制器。 讓模型管理數據,視圖顯示數據,控制器調停更新數據並重新繪製
顯示。

這裏寫圖片描述

mvc流程圖[2]

適用範圍


使用模型 - 視圖 - 控制器模式

  • 您希望將域數據與其用戶界面表示方式分開

權威信息


57.模型-視圖-主持人(Model-View-Presenter)

標題 類別 難度
模型-視圖-主持人(Model-View-Presenter) 持久層 中等難度

意圖


應用“關注點分離”原則,允許開發人員構建和測試用戶界面。

這裏寫圖片描述

mvp[2]流程圖
這裏寫圖片描述

適用範圍


在下列使用MVP情況
* 當你想改進表示邏輯中的“分離關注”原則時
* 當用戶界面開發和測試是必要的。

真實例子

58.模塊化(Module)

標題 類別 難度
模塊化(Module) 創建型 簡單難度

意圖


模塊模式用於實現由模塊化編程定義的軟件模塊的概念,在具有不完全直接支持概念的編程語言中。

這裏寫圖片描述

適用範圍


模塊模式可以被認爲是一種創意模式和一種結構模式。 它管理其他元素的創建和組織,並將其組織爲結構模式。
應用此模式的對象可以提供等效的命名空間,爲靜態類或具有更乾淨,更簡潔的語法和語義的靜態成員提供初始化和完成過程。

權威信息


59.Monad

標題 類別 難度
Monad(這個怎麼翻譯?) 其他 專家難度,函數式

意圖


基於線性代數的Monad模式表現出一步一步的鏈接操作的樣子。只要保證“同類型”約束,綁定函數可以描述爲將其輸出傳遞給另一個輸入。 正式地,monad由一個類型構造函數M和兩個操作符組成:
bind - 將monadic對象和一個函數從plain對象轉換爲monadic值並返回monadic值
return - 它採用普通類型的對象,並返回包含在一個monadic值中的對象。
(這方面需要函數式編程方面的知識。。博主只使用過rxjava,有點難描述)
這裏寫圖片描述

適用範圍


在以下任何一種情況下使用Monad

  • 當您想要輕鬆地鏈接操作時
  • 當你想應用每個函數,不管其中任何一個的結果

權威信息


60.單一的狀態(monostate)

標題 類別 難度
單一的狀態(monostate) 創建型 簡單難度

衆所周知


Borg

意圖


執行一個行爲,例如在所有實例之間分享相同的狀態(這裏分享request)
這裏寫圖片描述

適用範圍


使用Monostate模式

  • 必須在類的所有實例上共享相同的狀態。
  • 通常,Monostate模式可能使用任何地方都可以使用Singleton。但是單例模式的使用不是透明的,
  • Monostate的用法相比較單例模式的最主要優勢。 子類可以根據需要裝飾共享狀態,因此可以提供與基類動態不同的行爲。

典型用例


  • 日誌類
  • 管理與數據庫的連接
  • 文件管理器

61.多例模式(Multiton)

標題 類別 難度
多例模式(Multiton) 創建型 簡單難度

衆所周知


Registry

意圖


確保一個類的實例數量有限,並提供一個全局的接入點。
這裏寫圖片描述

適用範圍


何時使用多例模式

  • 必須持有特定數量的類的實例,並且它們可以從一個共同的接入點訪問客戶端

62.靜音模式(mute)

標題 類別 難度
靜音模式(mute) 其他 簡單難度,術語

意圖


提供一個模板來抑制任何被聲明但不能發生或只應該被記錄的異常;
同時執行一些業務邏輯。 該模板無需編寫重複的try-catch塊。

alt text

適用範圍


何時使用這個術語

  • 一個API聲明一些異常,但永遠不會拋出該異常。 ByteArrayOutputStream批量寫入方法。
  • 您需要通過記錄來抑制某些異常,例如關閉資源。

權威信息


63.互斥模式

標題 類別 難度
互斥模式(Mutex) 併發 中等難度

衆所周知


Mutual Exclusion Lock
Binary Semaphore

意圖


創建一個在任何一個時刻只允許單個線程訪問資源的鎖。
這裏寫圖片描述

適用範圍


何時使用互斥

  • 您需要同時防止兩個線程訪問臨界區
  • 併發訪問資源可能導致競爭條件

權威信息


64. 裸露的對象

標題 類別 難度
裸露的對象(Naked Objects) 結構型 專家難度

意圖


裸露的對象架構模式非常適合快速原型。 使用模式,只需要編寫域對象,
一切都是由框架自動生成的。
這裏寫圖片描述

適用範圍


使用裸體對象模式

  • 你是原型,需要快速的開發週期
  • 自動生成的用戶界面
  • 您要自動將域發佈爲REST服務

好的例子


權威信息


65.空對象

標題 類別 難度
空對象(Null Object) 行爲型 簡單難度

意圖


在大多數面向對象的語言中,如Java或C#,引用可能爲null。 在調用任何方法之前需要檢查這些引用以確保它們不爲空
,因爲通常無法調用空引用的方法。 而不是使用空引用來表示沒有對象(例如,不存在的客戶),使用一個對象實現預期的接口,但其方法體是空的。該這種方法優於一個工作的默認實現是一個Null對象是非常可預測的,沒有副作用:它什麼都不做。
這裏寫圖片描述

適用範圍


何時使用空對象模式

  • 你想避免顯式的空檢查,保持算法的優雅和易讀。

權威信息


66 .母對象(object-mother)

標題 類別 難度
母對象(object-mother) 創建型 簡單難度

意圖


使用分離的構建器和工廠的接口定義不可變的內容。

這裏寫圖片描述

適用範圍


何時使用母對象

  • 您希望在多個測試中保持一致的對象
  • 你想減少在測試中創建對象的代碼
  • 每個測試都應該運行新的數據

權威信息

67. 對象池(object-pool)

標題 類別 難度
對象池(object-pool) 創建型 簡單難度,性能

意圖


當對象創建成本高昂時,並且它只需要在短時間內被使用,這時對象池模式是有用處的。對象池爲實例化的對象提供了緩存,跟蹤哪些對象正在使用,哪些是可用的。

這裏寫圖片描述

適用範圍


使用對象池模式

  • 對象創建成本高(分配成本)
  • 你需要大量的短命對象(內存碎片)

68. 觀察者模式

標題 類別 難度
Observer(觀察者模式) 創建型 簡單難度,GOF,響應式

衆所周知


Dependents, Publish-Subscribe

意圖


定義對象之間的一對多依賴關係, 被觀察的對象狀態改變,其所有依賴關係將被通知和更新自動。

這裏寫圖片描述

適用範圍


在以下情況下使用觀察者模式

  • 抽象有兩個方面,一個取決於另一個方面。 將這些方面封裝在單獨的對象中,可以獨立地改變和重用它們
  • 當對一個對象的更改需要更改其他對象時,您不需要知道更改多少個對象
  • 當對象應該能夠通知其他對象而不對這些對象是誰做出假設的。 換句話說,你不希望這些對象緊密耦合。

典型用例

  • 改變一個對象會導致其他對象發生變化

真實世界


權威信息


69. 頁對象

標題 類別 難度
頁對象(page-object) 測試 中等難度

意圖


頁面對象封裝了UI,隱藏應用程序(通常是Web應用程序)的底層UI小部件,並提供特定於應用程序的API,以允許操作測試所需的UI組件。 在這樣做時,它允許測試類本身專注於測試邏輯。

這裏寫圖片描述

適用範圍


何時使用頁面對象模式

  • 您正在爲Web應用程序編寫自動化測試,並希望將測試所需的UI操作與實際測試邏輯分開。
  • 使您的測試不那麼脆弱,更可讀性更強

權威信息


70. 頁對象

標題 類別 難度
頁對象(page-object) 結構型 簡單難度

意圖


根據需要從服務器發送部分響應。 客戶端將指定它需要的字段,而不是要求服務端提供資源的所有細節。

這裏寫圖片描述

適用範圍


使用部分響應模式

  • 客戶端只需要來自資源的數據子集。
  • 避免過多的數據傳輸

權威信息


71. poison-pill

標題 類別 難度
poison-pill(不知道怎麼翻譯)) 其他 中等難度,響應式

意圖


poison-pill允許提供的已知預定義數據項,生產者與消耗者分離,並提供優雅的方式結束進程

這裏寫圖片描述

適用範圍


何時使用poison-pil術語

  • 需要將信號從一個線程/進程發送到另一個線程 ,並在另一個線程終止

真實例子


72. 私有類數據

標題 類別 難度
Private Class Data(私有類數據) 其他 簡單難度,術語

意圖


私人數據設計模式旨在減少曝光屬性通過限制其可見性。 它減少了類的數量
通過將屬性封裝在單個Data對象中。
這裏寫圖片描述

適用範圍


何時使用私有類數據模式

  • 你想阻止對類數據成員的寫訪問

73. 生產者-消費者

標題 類別 難度
生產者-消費者(producer-consumer) 併發 中等難度,I/O,響應

意圖


生產者消費者設計模式是一種經典的併發模式,降低了生產者與消費者之間的耦合,通過分離實際的工作執行。
這裏寫圖片描述

適用範圍


何時使用生產者消費者術語

  • 通過分離生產和消耗兩個過程,達到解耦系統的目的。
  • 解決生產工作或消費工作在不同時間要求的問題

74. 承諾

標題 類別 難度
承諾(promise) 併發 中等難度,函數,響應

意圖


承諾模式代表一個值但事先卻不需要知道它。允許你通過承諾模式關聯一個異步動作的最終成功值或失敗的原因。提供了一種異步訪問的方式,但表現出同步執行的樣子

這裏寫圖片描述

適用範圍


某些工作需要異步完成時,承諾模式適用於並行編程
和:

  • 代碼可維護性和可讀性受到回調地獄的困擾。
  • 您需要編寫承諾,並需要更好的錯誤處理異步任務。
  • 你想使用函數式的編程風格。

真實例子


關聯模式


  • Async Method Invocation
  • Callback

權威信息


75. 屬性

標題 類別 難度
屬性(property) 創建型 簡單

意圖


使用已經存在的對象(當做父類)創建對象層級和新的對象
這裏寫圖片描述

適用範圍


何時使用屬性模式

  • 當你想動態的創建對象的屬性和原型繼承對象

真實例子


76. 原型

標題 類別 難度
原型(prototype) 創建型 GOF,簡單

意圖


使用原型指定要創建的對象的種類實例,並通過複製此原型創建新對象。

解釋


真實世界

還記得多莉嗎 被克隆的羊! 讓我們不瞭解細節,但這裏的關鍵在於它是如何克隆的。

字面意思

通過克隆創建基於現有對象的對象。

維基解釋

原型圖是軟件開發中的創建型設計模式。 當要創建的對象的類型由原型實例確定時,它將被克隆以生成新對象。

簡而言之,它允許您創建現有對象的副本並根據需要進行修改,而不是從頭開始創建對象並進行設置。
編程例子

在Java中,可以通過從Object實現Cloneable並覆蓋clone來輕鬆實現

class Sheep implements Cloneable {
  private String name;
  public Sheep(String name) { this.name = name; }
  public void setName(String name) { this.name = name; }
  public String getName() { return name; }
  @Override
  public Sheep clone() throws CloneNotSupportedException {
    return new Sheep(name);
  }
}

然後可以像下面那樣進行克隆

Sheep original = new Sheep("Jolly");
System.out.println(original.getName()); // Jolly


Sheep cloned = original.clone();
cloned.setName("Dolly");
System.out.println(cloned.getName()); // Dolly

適用範圍


當系統應獨立於其產品的創建,組成和代表的方式時,使用原型模式; 和

  • 當運行時指定要實例化的類時,例如,通過動態加載
  • 避免建立與產品類層次結構相似的工廠的類層次結構
  • 當一個類的實例可以只有幾個不同的狀態組合中的一個時。 安裝相應數量的原型並克隆它們可能更方便,而不是手動實例化類,每次都有適當的狀態
  • 與克隆相比,創建對象比較昂貴

真實例子


權威信息


77.代理

標題 類別 難度
proxy(代理) 結構型 GOF,簡單

意圖


爲另一個對象提供代理或佔位符來控制訪問它

解釋


真實例子

想象一個法術塔,允許當地的巫師去研究他們的法術。 象牙塔只能通過代理訪問,確保只有前三個巫師才能進入。 這裏代理代表了塔的功能,並添加了訪問控制。

字面意思

使用代理模式,一個類代表另一個類的功能。

維基描述

代理,最通用的形式是一個類作爲其他東西的接口。 代理是由客戶端調用來訪問幕後真實服務對象的包裝器或代理對象。 使用代理可以簡單地轉發到真實的對象,或者可以提供額外的邏輯。 在代理中,可以提供額外的功能,例如當真實對象上的操作是資源密集型時的緩存,或者在調用真實對象的操作之前檢查前提條件。

編程例子

從上面我們的網頁上股塔示例。 首先我們有巫師塔接口和象牙塔類

public interface WizardTower {

  void enter(Wizard wizard);
}

public class IvoryTower implements WizardTower {

  private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);

  public void enter(Wizard wizard) {
    LOGGER.info("{} enters the tower.", wizard);
  }

}

簡單的巫師類

public class Wizard {

  private final String name;

  public Wizard(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return name;
  }
}

我們通過代理控制巫師塔的進入

public class WizardTowerProxy implements WizardTower {

  private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);

  private static final int NUM_WIZARDS_ALLOWED = 3;

  private int numWizards;

  private final WizardTower tower;

  public WizardTowerProxy(WizardTower tower) {
    this.tower = tower;
  }

  @Override
  public void enter(Wizard wizard) {
    if (numWizards < NUM_WIZARDS_ALLOWED) {
      tower.enter(wizard);
      numWizards++;
    } else {
      LOGGER.info("{} is not allowed to enter!", wizard);
    }
  }
}

這裏是塔進入場景

WizardTowerProxy proxy = new WizardTowerProxy(new IvoryTower());
proxy.enter(new Wizard("Red wizard")); // 進去.
proxy.enter(new Wizard("White wizard")); //進去.
proxy.enter(new Wizard("Black wizard")); // 進去.
proxy.enter(new Wizard("Green wizard")); // 進不去!
proxy.enter(new Wizard("Brown wizard")); // 進不去!

適用範圍


通過代理模式來使用更多變化或複雜的對象。 這裏
是代理模式適用的幾種常見情況

  • 遠程代理爲不同地址空間中的對象提供本地代理。
  • 虛擬代理根據需要創建昂貴的對象。
  • 保護代理控制對原始對象的訪問。 當對象具有不同的訪問權限時,保護代理是有用的。

典型用例

  • 控制對另一個對象的訪問
  • 懶加載
  • 實施日誌記錄
  • 方便網絡連接
  • 計數對象的引用

目錄


演講


真實世界


權威信息


78.發佈-訂閱

標題 類別 難度
發佈-訂閱(publish-subscribe) 結構型 EIP,Apache Camel™

意圖


發送者發送廣播信息至對相關信息感興趣的接收者

這裏寫圖片描述

適合範圍


何時使用發佈訂閱頻道模式

  • 兩個或多個應用程序需要使用消息系統進行通信以進行廣播。

權威信息


79.基於隊列的負載均衡

標題 類別 難度
基於隊列的負載均衡(queue-load-leveling) 其他 中等難度,性能

意圖


使用一個隊列,作爲一個任務和它調用的服務之間的緩衝區,以便順利進行可能導致服務失敗或任務超時的間歇性重負載。這種模式可以幫助最大限度地減少需求峯值對可用性和響應能力的影響
用於任務和服務。
這裏寫圖片描述

適用範圍


  • 此模式非常適合任何使用可能會重載的服務的應用程序。
  • 如果應用程序期望來自服務的響應以最小延遲,則此模式可能不合適。

目錄


真實例子


  • Microsoft Azure Web角色通過使用單獨的存儲服務來存儲數據。 如果Web角色的大量實例併發運行,則存儲服務可能會被淹沒,並且無法快速響應請求,以防止這些請求超時或失敗。

權威信息


80.響應器

標題 類別 難度
響應器(reactor) 併發 專家難度,I/O

意圖


反應器設計模式處理由一個或多個客戶端同時傳遞到應用程序的服務請求。 應用程序可以註冊特定的事件處理器來處理特定事件。 反應器持有事件分發器,並分發事件給特定的事件處理器。
這裏寫圖片描述

適用範圍


使用反應器模式

  • 服務器應用程序需要處理來自多個客戶端的併發服務請求。
  • 即使處理較舊的客戶端請求,服務器應用程序需要可用於從新客戶端接收請求。
  • 服務器必須最大限度地提高吞吐量,最大限度地減少延遲並有效利用CPU而無需阻塞

真實例子


權威信息


81.讀寫鎖

標題 類別 難度
響應器(reactor) 併發 中等難度,性能

意圖

假設我們有一個具有上面詳細描述的基本約束的共享內存區域。 可以保護互斥互斥體之後的共享數據,在這種情況下,兩個線程不能同時訪問數據。 但是,這個解決方案不是最佳的,因爲讀取器R1可能有鎖,然後另一個讀取器R2請求訪問。 在開始自己的閱讀操作之前,R2等待R1完成是愚蠢的; 相反,R2應該立即開始。 這是Reader Writer Lock模式的動機。

這裏寫圖片描述

適用範圍

應用程序需要增加多線程的資源同步性能,特別是有混合的讀/寫操作。

真實例子


權威信息


82.倉庫

標題 類別 難度
倉庫(repository) 持久層 中等難度,spring

意圖


在域和數據映射之間添加存儲庫層層隔離域對象與數據庫訪問代碼的細節
以最小化查詢代碼的散佈和重複。 存儲庫模式是特別適用於域類大或重的系統
利用查詢。

這裏寫圖片描述

適用範圍


何時使用Repository模式

  • 域對象數量很大
  • 你想避免重複查詢代碼
  • 您希望將數據庫查詢代碼保存在一個位置
  • 您有多個數據源
  • *

真實例子


權威信息


83.資源獲取初始化

標題 類別 難度
資源獲取初始化(resource-acquisition-is-initialization) 其他 簡單難度,術語

意圖


資源獲取初始化模式可用於實現資源異常安全管理。
這裏寫圖片描述

解釋


編程例子

public class App {

  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);


  public static void main(String[] args) throws Exception {

    try (SlidingDoor slidingDoor = new SlidingDoor()) {
      LOGGER.info("Walking in.");
    }

    try (TreasureChest treasureChest = new TreasureChest()) {
      LOGGER.info("Looting contents.");
    }
  }
}

適用範圍


用資源獲取初始化模式

  • 您的資源必須在每個條件下關閉

84.信號

標題 類別 難度
信號(semaphore) 併發 中等難度

意圖


創建一箇中介對資源池的訪問的鎖。在有限數量的線程條件下,在創建時指定一定數量的
信號量,可以在任何給定的時間訪問資源。一個只允許一個併發訪問資源的信號量
被稱爲二進制信號量。
這裏寫圖片描述

適用範圍


何時使用信號量時

  • 你有一個資源池來分配給不同的線程
  • 併發訪問資源可能導致競爭條件

權威信息


85.僕人

標題 類別 難度
僕人(servant) 結構型 簡單難度

意圖


僕人用於爲一組類提供一些行爲。而不是在每個類中定義該行爲 - 或者當我們無法確定
在普通父類中使用這種行爲 , 它在僕人中定義一次。

這裏寫圖片描述

適用範圍


時使用僕人模式

  • 當我們希望一些對象執行一個常見的動作,並且不希望將這個動作定義爲每個類中的一個方法。

權威信息


86.服務層

標題 類別 難度
服務層(service-layer) 結構型 中等難度

意圖


服務層是域邏輯的抽象。通常應用程序需要多種接口來存儲和存儲數據
實現邏輯:數據加載器,用戶界面,集成網關和其他。 儘管它們的目的不同,但是這些接口通常需要通用
與應用程序的交互訪問和操縱其數據並調用其業務邏輯。 服務層實現了這一角色。
這裏寫圖片描述

適用範圍


何時使用服務層模式

  • 您希望在API下封裝域邏輯
  • 您需要實現具有通用邏輯和數據的多個接口

權威信息


87.服務定位

標題 類別 難度
服務定位(service-locator) 結構型 簡單難度,性能

意圖


封裝服務層的獲取執行過程。
這裏寫圖片描述

適用範圍


服務定位器模式適用於任何時候使用通常是冗餘的JNDI來定位/獲取各種服務
和昂貴的查詢。 服務定位器模式解決了這個昂貴的問題通過使用緩存技術來查找。 爲了第一次
請求特定服務,服務定位器在JNDI中查找,取出相關服務,然後最終緩存此服務對象。 現在進一步
通過服務定位器查找相同的服務在其緩存中完成在很大程度上提高了應用的性能。

典型用例


  • 當網絡點擊費用高昂且耗時時
  • 查詢服務相當頻繁
  • 正在使用大量的服務

結論


  • 違反了接口隔離原則,可能像客戶端提供了他們不需要的服務
  • 在運行時創建服務可能導致客戶端崩潰

權威信息


89.單例

標題 類別 難度
單例(singleton) 結構型 簡單難度,性能

意圖


確保一個類只有一個實例,並提供一個全局點訪問它

解釋


真實例子

只有一個象牙塔,巫師研究他們的魔法。 巫師一直使用同樣的魔法象牙塔。 這裏的象牙塔是單例。

字面意思

確保只創建一個特定類的一個對象。

維基解釋

在軟件工程中,單例模式是將類的實例化限制爲一個對象的軟件設計模式。 當需要一個對象來協調整個系統的動作時,這是非常有用的。

編程例子
Joshua Bloch,Effective Java 2nd Edition第18頁

單元素枚舉類型是實現單例的最佳方法

public enum EnumIvoryTower {

  INSTANCE;

  @Override
  public String toString() {
    return getDeclaringClass().getCanonicalName() + "@" + hashCode();
  }
}

使用

EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE;
EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE;
assertEquals(enumIvoryTower1, enumIvoryTower2); // true

不安全單例

public final class IvoryTower {


  private IvoryTower() {}


  private static final IvoryTower INSTANCE = new IvoryTower();


  public static IvoryTower getInstance() {
    return INSTANCE;
  }

安全懶加載單例

public final class ThreadSafeLazyLoadedIvoryTower {

  private static ThreadSafeLazyLoadedIvoryTower instance;

  private ThreadSafeLazyLoadedIvoryTower() {
  // to prevent instantiating by Reflection call
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }


  public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {

    if (instance == null) {
      instance = new ThreadSafeLazyLoadedIvoryTower();
    }

    return instance;
  }
}

雙重檢查鎖單例

public final class ThreadSafeDoubleCheckLocking {

  private static volatile ThreadSafeDoubleCheckLocking instance;


  private ThreadSafeDoubleCheckLocking() {
    // to prevent instantiating by Reflection call
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }


  public static ThreadSafeDoubleCheckLocking getInstance() {


    ThreadSafeDoubleCheckLocking result = instance;
    //檢查單例實例是否已初始化。 如果它被初始化,那麼我們可以返回實例。
    if (result == null) {
      //它沒有初始化,但是我們不能確定,因爲一些其他線程可能已初始化它
      // 同時。 所以要確保我們需要鎖定一個對象以獲得互斥。
      synchronized (ThreadSafeDoubleCheckLocking.class) {
        //再次將實例分配給局部變量,以檢查它是否被某個其他線程初始化
        //當前線程被阻止進入鎖定區域。 如果它被初始化,那麼我們可以
        //返回之前創建的實例,就像以前的空檢查一樣。
        result = instance;
        if (result == null) {
          //實例還沒有被初始化,所以我們可以安全地(沒有其他線程可以進入這個區域)
          //創建一個實例,使其成爲我們的單例實例。
          instance = result = new ThreadSafeDoubleCheckLocking();
        }
      }
    }
    return result;
  }
}

內部靜態類成員持有實例

public final class InitializingOnDemandHolderIdiom {


  private InitializingOnDemandHolderIdiom() {}


  public static InitializingOnDemandHolderIdiom getInstance() {
    return HelperHolder.INSTANCE;
  }


  private static class HelperHolder {
    private static final InitializingOnDemandHolderIdiom INSTANCE =
        new InitializingOnDemandHolderIdiom();
  }
}

適用範圍


何時Singleton模式

  • 必須只有一個類的一個實例,並且它必須可以從衆所周知的接入點訪問客戶端
  • 當唯一的實例應該通過子類化可擴展時,客戶端應該能夠使用擴展實例而不修改它們的代碼

典型用例


  • 記錄類
  • 管理與數據庫的連接
  • 文件管理器

真實例子


結論

  • 控制自己的創建和生命週期來違反了單一責任原則(SRP)。
  • 鼓勵使用全局共享實例,導致此對象使用的對象和資源很難被釋放。
  • 創建緊密耦合的代碼。 Singleton的客戶變得難以測試。
  • 幾乎不可能對Singleton進行子類化。

權威信息


89.規範

標題 類別 難度
規範(specification) 行爲型 簡單難度

意圖


規範模式分離如何匹配一個候選的對象組。 它是有作用的對於,值的校驗和綁定命令。
這裏寫圖片描述

適用範圍


何時使用規格圖樣

  • 您需要根據某些條件選擇對象的子集,並在不同時間刷新選擇
  • 您需要檢查只有適當的對象用於某個角色(驗證)

關聯模式


  • Repository

權威信息


90.狀態

標題 類別 難度
狀態(state) 行爲型 中等難度,GOF

意圖


允許對象在其內部狀態時更改其行爲變化。 該對象將會改變其類。
這裏寫圖片描述

適用範圍


在以下任何一種情況下使用狀態模式

  • 對象的行爲取決於其狀態,並且必須根據該狀態在運行時更改其行爲
  • 操作具有取決於對象狀態的大型多部分條件語句。 該狀態通常由一個或多個枚舉的常數表示。 通常,幾個操作將包含相同的條件結構。 狀態模式將條件的每個分支放在一個單獨的類中。 這使您可以將對象的狀態視爲本身可以獨立於其他對象的對象。

真實例子


權威信息


91.一步一步創建者

標題 類別 難度
一步一步創建者(step-builder) 創建型 中等難度

意圖


Builder模式的擴展,完全引導用戶創建對象,沒有混淆的機會。
用戶體驗將會因爲只能看到下一步方法可用,而不是構建方法直到構建對象纔是正確的時間,因此將會得到更多的改進。

這裏寫圖片描述

適用範圍


當創建複雜對象的算法應該獨立於組成對象的部分以及組合方式時,使用Step Builder模式,構造過程必須允許在構造順序的過程中構造的對象的不同表示。

權威信息


92.策略

標題 類別 難度
策略(strategy) 行爲型 GOF,簡單難度

意圖


定義一系列算法,封裝每一個算法,確保他們可以通用。 策略模式讓算法的變化獨立與客戶端的使用。

這裏寫圖片描述

適用範圍


使用策略模式

  • 許多相關的類在其行爲上有所不同。 策略提供了一種將類配置爲許多行爲之一的方法
  • 您需要一個算法的不同變體。 例如,您可以定義反映不同空間/時間權衡的算法。 當這些變體被實現爲算法的類層次結構時,可以使用策略
  • 一個算法使用客戶端不應該知道的數據。 使用策略模式避免暴露覆雜的算法特定的數據結構
  • 一個類定義了許多行爲,並且它們在其操作中顯示爲多個條件語句。 而不是許多條件,將相關的條件分支移動到自己的策略類中

權威信息


93.模板方法

標題 類別 難度
模板方法(template-method) 行爲型 GOF,簡單難度

意圖


在操作中定義算法的骨架,推遲一些算法步驟到子類。 模板方法讓子類重新定義某些步驟
,一種不改變算法結構的方式。

這裏寫圖片描述

適用範圍


何時使用模板方法模式

  • 實現一個算法的不變部分,並將其留給子類來實現可以變化的行爲
  • 當子類中的常見行爲應該被考慮並定位在公共類中以避免代碼重複時。 這是Opdyke和Johnson所描述的“重構泛化”的好例子。 您首先識別現有代碼中的差異,然後將差異分爲新操作。 最後,您使用調用其中一個新操作的模板方法替換不同的代碼
  • 控制子類擴展。 您可以定義一個在特定點調用“鉤”操作的模板方法,從而僅在這些點允許擴展

權威信息


94.線程池

標題 類別 難度
線程池(thread-pool) 行爲型 性能,中等難度

意圖


通常情況下要執行的任務是短暫的並且任務數量大。 爲每個任務創建一個新線程
系統花費更多的時間而不是執行實際任務。 線程池通過重用現有的線程來解決這個問題
並消除創建新線程的延遲。
這裏寫圖片描述

適用範圍


使用線程池模式

  • 您有大量短命的任務並行執行

95.節流

標題 類別 難度
節流(throttling) 行爲型 簡單難度

意圖


在超過服務端分配限制的情況下,限制客戶端的訪問

適用範圍


應使用節流模式:

  • 需要限制服務訪問對服務性能沒有很大的影響。
  • 當多個客戶端消耗相同的服務資源,並且必須根據每個客戶端的使用量進行限制。

95.線程本地存儲

標題 類別 難度
線程本地存儲(Thread Local Storage) 併發型 中等難度

意圖


將變量保存到線程,以免被其他線程損壞。 如果您在Callable對象或Runnable對象中使用不是隻讀的類變量或靜態變量,則需要這樣做。
這裏寫圖片描述

適用範圍


在以下任何情況下使用線程本地存儲

  • 當您在Callable / Runnable對象中使用不是隻讀的類變量,並且在並行運行的多個線程中使用相同的Callable實例。
  • 當您在Callable / Runnable對象中使用非只讀的靜態變量,並且Callable / Runnable的多個實例可能並行運行時。

96.寬容閱讀

標題 類別 難度
寬容閱讀(tolerant-reader) 交互 簡單難度

意圖


寬容閱讀是一種有助於創建的集成健壯的通信系統。 這個想法是儘可能的寬容從另一個服務讀取數據。 這樣,當通信模式改變,接收者一定不會崩潰。
這裏寫圖片描述

適用範圍


使用寬容閱讀模式

  • 通信模式可以演變和改變,而接收方不應該崩潰

權威信息


97. 雙模式

標題 類別 難度
雙胞胎(twin) 創建型 中等難度

意圖


雙模式是一種設計模式,提供了一個標準的解決方案去模擬多繼承

這裏寫圖片描述

適用範圍


何時使用雙模式術語

  • 不支持此功能的語言模擬多重繼承。
  • 避免某些多重繼承的問題,如名稱衝突。

權威信息


98. 值對象

標題 類別 難度
值對象(value-object) 創建型 簡單難度

意圖


提供值語義而不是引用語義的對象。
這意味着價對象的equal不是等於兩個對象相同。 兩個值對象是
當它們具有相同的值時,不一定是相同的對象。
這裏寫圖片描述

適用範圍


何時使用值對象

  • 您需要根據對象的值來衡量對象的等式

真實例子


權威信息


99. 訪問者

標題 類別 難度
訪問者(visitor) 行爲型 GOF,中等難度

意圖


表示要對對象的元素執行的操作結構體。 訪問者可以在不更改類的情況下定義新操作
的操作元素。

適用範圍


何時使用訪客模式

  • 對象結構包含許多具有不同接口的對象類,並且您希望對這些對象執行依賴於其具體類的操作
  • 需要對對象結構中的對象執行許多不同且不相關的操作,並且您希望避免使用這些操作“污染”其類。 訪問者可以通過在一個類中定義相關操作來保持相關操作。 當對象結構由許多應用程序共享時,使用Visitor將操作放在需要它們的應用程序中
  • 定義對象結構的類很少更改,但是您經常希望在結構上定義新的操作。 更改對象結構類需要重新定義所有訪問者的接口,這可能是昂貴的。 如果對象結構類經常發生變化,那麼最好在這些類中定義操作

真實例子


權威信息


引用


[1]設計模式沉思錄
[2]MVC vs. MVP vs. MVVM on Android
[3]java 設計模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章