設計模式


完整的設計模式筆記,請參考這兒

一. UML時序圖和類圖

1. 類之間的關係

六種類的關係:

  1. 泛化關係(generalization)
  2. 實現關係(realize)
  3. 聚合關係(aggregation)
  4. 組合關係(composition)
  5. 關聯關係(association)
  6. 依賴關係(dependency)

UML類圖實例

1.1. 泛化關係

泛化與實現關係同屬於類的繼承結構。(泛化和實現)繼承關係爲 is-a的關係;兩個對象之間如果可以用 is-a 來表示,就是繼承關係:(…是…)
Example:SUV是汽車,汽車和SUV之間時泛化關係。泛化關係表現爲繼承非抽象類。
泛化關係圖例

1.2. 實現關係

"車"是一個抽象概念,在現實中並無法直接用來定義對象;只有指明具體的子類(汽車還是自行車),纔可以用來定義對象.
Example:汽車、自行車與車(抽象類)之間是實現關係,實現關係表現爲繼承抽象類.
實現關係圖例

1.3. 聚合關係

聚合關係用於表示實體對象之間的關係,表示整體由部分構成的語義;
Exampl:一個部門由多個員工組成,員工和部門之間是聚合關係
聚合關係示意圖

1.4. 組合關係

與聚合關係一樣,組合關係同樣表示整體由部分構成的語義;但組合關係是一種強依賴的特殊聚合關係,如果整體不存在了,則部分也將不存在;
Example:公司由多個部門組成,,公司不存在了,部門也將不存在了;因此,公司和部門是組合關係;
組合關係示意圖

1.5. 關聯關係

它描述不同類的對象之間的結構關係;它是一種靜態關係, 通常與運行狀態無關,一般由常識(約定俗成)等因素決定的;它一般用來定義對象之間靜態的、天然的結構; 所以,關聯關係是一種“強關聯”的關係;
Example:乘車人和車票之間就是一種關聯關係;學生和學校就是一種關聯關係;
關聯關係示意圖
注:在最終代碼中,關聯對象通常是以成員變量的形式實現的;

1.6. 依賴關係

它描述一個對象在運行期間會用到另一個對象的關係;與關聯關係不同的是,它是一種臨時性的關係,通常在運行期間產生,並且隨着運行時的變化; 依賴關係也可能發生變化;
Example:A依賴於B,且它們之間是單向依賴。雙向依賴是一種非常糟糕的結構,我們總是應該保持單向依賴,杜絕雙向依賴的產生;
依賴關係示意圖
注:在最終代碼中,依賴關係體現爲類構造方法類方法的傳入參數,箭頭的指向爲調用關係;依賴關係除了臨時知道對方外,還可“使用”對方的方法和屬性;

2. 時序圖

時序圖(Sequence Diagram)是顯示對象之間交互的圖,這些對象是按時間順序排列的。時序圖中顯示的是參與交互的對象及其對象之間消息交互的順序。
時序圖包括的建模元素主要有:對象(Actor)、生命線(Lifeline)、控制焦點(Focus of control)、消息(Message)等等。

二. 創建型模式

創建型模式對類的實例化過程進行抽象,能夠將軟件模塊中對象的創建對象的使用分離。爲了使模塊的結構清晰,外界對於這些對象只需要知道它們共同的接口,而不需要清楚其具體的實現細節。

包含的模式:

  • 簡單工廠模式(Simple Factory)
  • 工廠方法模式(Factory Method)
  • 抽象工廠模式(Abstract Factory)
  • 建造者模式(Builder)
  • 原型模式(Prototype)
  • 單例模式(Singleton)

1. 簡單工廠模式

1.1. 模式動機

一個軟件系統可以提供多個外觀不同的按鈕(如圓形按鈕、矩形按鈕、菱形按鈕等),這些按鈕源於同一個基類,只是不同子類在繼承基類後修改部分屬性。如果我們希望在使用這些按鈕時,不需要知道這些具體按鈕類的名字,只需要知道表示該按鈕類的一個參數,並提供一個調用方便的方法,把該參數傳入方法即可返回一個相應的按鈕對象使得其呈現不同外觀。

1.2. 模式定義

簡單工廠模式,又稱爲靜態工廠模式。在簡單工廠模式中,可以根據參數的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的類實例通常具有共同父類,只是在子類中作不同的實現,以體現屬性的差異。

1.3. 模式結構

簡單工廠模式包含如下角色:

  • Factory:工廠角色
    工廠角色負責實現創建所有實例的內部邏輯–工廠類;
  • Product:抽象產品角色
    抽象產品角色是所創建的所有對象的父類,負責描述所有實例所共有的公共接口–抽象父類;
  • ConcreteProduct:具體產品角色
    具體產品角色是創建的目標,所創建的duixiang都充當這個角色的某個具體類的實例–具體子類。

簡單工廠示意圖

1.4. 時序圖

簡單工廠時序圖

1.5. 簡單工廠模式的優缺點

優點:

  • 工廠類含有必要的判斷邏輯,可以決定在什麼時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而只是消費產品;
  • 客戶端無需知道所創建的具體產品類的類名,更不關心具體的創建細節,只需要知道具體產品類的對應參數;
  • 通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類。

缺點:

  • 工廠類集中了所有產品的創建邏輯,一旦不能工作,整個系統都要受到影響;
  • 使用簡單工廠模式會增加系統中類的個數,增加了系統的複雜度–廢話;
  • 系統擴展困難,一旦添加新產品需要修改工廠類邏輯;
  • 簡單工廠模式使用靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構;

1.6. 適用環境

在以下情況可以使用簡單工廠模式:

  • 工廠類負責創建的對象比較少:由於創建的對象較少,不會造成工廠方法的業務邏輯過於複雜;
  • 客戶端只知道傳入工廠類的參數,對如何創建對象不關心:客戶端不需要關心類的創建細節,甚至不需要知道類名,只需要知道類型對應的參數。

1.7. 代碼示例

//工廠類
public class Factory {
    //靜態工廠方法
    public static Product createProduct(String proname){
        if (proname=="A"){
            return new ConcreteProductA();
        }else if (proname=="B"){
            return new ConcreteProductB();
        }
        return null;
    }
    public static void main(String[] arg){
       Product productA = Factory.createProduct("A");
       productA.use();
    }
}
//抽象產品父類
public abstract class Product {
    private int type;
    public abstract void use();
}
//具體產品類子類
class ConcreteProductA extends Product {
    @Override
    public void use() {
        System.out.println("Create a ProductA");
    }
}

//輸出結果
Create a ProductA

2. 工廠方法模式

2.1. 模式動機

現在對系統進行修改,不再設計一個按鈕工廠類來統一負責所有產品的創建,而是將具體按鈕的創建過程交給具體工廠子類完成。我們定義一個抽象的按鈕工廠類,再定義具體的工廠子類來生成圓形、矩形、菱形等不同類型的按鈕,它們對抽象工廠類中的生產方法進行具體的實現。這種模式結構可以在不修改具體工廠類的情況下引進新的產品,只需要爲新類型的按鈕創建一個具體的工廠子類來獲取新的按鈕實例。有利於類的擴展和維護

2.2. 模式定義

工廠方法模式又稱爲工廠模式,也叫虛擬構造器(Vertical Constructor)模式或者多態工廠(Polymorphic Factory)模式。在工廠方法模式中,工廠父類負責定義創建產品對象的公共接口,而工廠子類負責生成具體的產品對象。這樣做的目的是將產品類的實例化操作延遲到工廠子類完成,即通過工廠子類來確定實現哪一個具體產品類。

2.3. 模式結構

工廠方法模式包含下列角色:

  • Product:抽象產品
  • ConcreteProduct:具體產品
  • Factory:抽象工廠
  • ConcreteFactory:具體工廠

工廠模式示意圖

2.4. 時序圖

工廠模式時序圖

2.5. 工廠方法模式的優缺點

優點

  • 在工廠方法模式中,用戶只需要關心所需產品對應的工廠,無須關心創建細節,甚至不需要知道具體產品類類名。
  • 基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠自主確定要創建的具體的產品對象,而如何創建這一對象的細節則完全封裝在具體工廠內部。工廠模式之所以被稱爲多態工廠模式,是因爲所有的具體工廠類都具有同一抽象父類。
  • 在系統中加入新產品時,只需要添加一個具體工廠和具體產品即可。因此,系統的可擴展性較好,符合“開閉原則”

缺點

  • 在添加新產品時,需要添加新的具體產品類,同時還要提供對應用於創建實例的具體工廠類。系統中類的個數成對增加,這在一定程度上增加了系統的複雜度。
  • 由於考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

2.6. 適用環境

在以下情況可以使用工廠方法模式:

  • 在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠類即可,具體的產品由具體的工廠類創建;
  • 一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
  • 將創建對象的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。

2.7. 代碼示例

//具體的工廠方法類
public class ConcreteFactory extends Factory {
    @Override
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
    public static void main(String[] arg){
        //具體的工廠方法
        Factory factory = new ConcreteFactory();
        Product product = factory.factoryMethod();
        product.use();
    }
}
//具體的產品類
public class ConcreteProduct extends Product {
    @Override
    public void use() {
        System.out.println("Create a Product");
    }
}
//抽象工廠類
abstract class Factory {
    public abstract Product factoryMethod();
}
//抽象產品類
abstract class Product {
    public abstract void use();
}

//執行結果
Create a Product

3. 抽象工廠模式

3.1. 模式動機

  • 在工廠方法模式中,具體工廠負責生產具體產品。而且在一般情況下,一個具體工廠只有一個工廠方法或一組重載的生產方法,對應生產一種具體產品。但是有時候我們需要一個工廠提供多個產品對象,而不是單一的產品對象。
  • 抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對一個產品等級結構,而抽象工廠模式針對多個產品等級結構。一個工廠等級結構可以負責多個不同產品等級結構中的產品對象的創建。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更爲簡單、高效。

引入概念:

  • 產品等級結構: 產品等級結構即產品的繼承結構。抽象產品和具體產品之間構成一個產品等級結構,抽象產品是父類,具體產品是子類。
  • 產品族: 在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構的一組產品。例如,海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構,海爾電冰箱位於電冰箱產品等級結構。

3.2. 模式定義

抽象工廠模式,提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。抽象工廠模式又稱爲Kit模式,屬於對象創建型模式。

3.3. 模式結構

  • AbstractFactory:抽象工廠
  • ConcreteFactory:具體工廠
  • AbstractProduct:抽象產品
  • Product:具體產品

抽象工廠模式示意圖

3.4. 時序圖

抽象工廠模式時序圖

3.5. 抽象工廠模式的優缺點

優點:

  • 抽象工廠模式隔離了具體類的生成,使客戶不需要知道什麼被創建。由於這種隔離,更換一個具體工廠變得容易。所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在一定程度上改變軟件系統的行爲。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的。
  • 當一個產品族中 的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象,這對一些需要根據當前環境來決定其行爲的軟件系統來說,是一種非常實用的設計模式。
  • 增加新的具體工廠和產品族很方便,無須修改已有系統,符合開閉原則

缺點:

  • 在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品,這是因爲在抽象工廠角色中規定了所有可能被創建的產品集合,要支持新種類的產品就意味着要對該接口進行擴展,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來不便。
  • 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)。

3.6. 適用環境

在以下情況適合採用抽象工廠模式:

  • 一個系統不應當依賴於產品實例被如何創建、組合和表達的細節,這對於所有類型的工廠模式都重要;
  • 系統中有多於一個的產品族,而每次只使用其中一個產品族;
  • 屬於同一產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來;
  • 系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於具體實現。

3.7. 代碼示例

//抽象工廠
public abstract class AbstractFactory {
    //生產不同產品等級結構的產品對象
    public abstract AbstractProductA createProductA();
    public abstract AbstractProductB createProductB();
}
//抽象產品--電視機
public abstract class AbstractProductA {
    protected static String type;
    protected static String name;

    public AbstractProductA() {
       type = "電視機";
    }
    public abstract void watch();
}
//抽象產品--電冰箱
public abstract class AbstractProductB {
    protected static String type;
    protected static String name;

    public AbstractProductB() {
        type = "電冰箱";
    }
    public abstract void use();
}
//電視機的具體產品--海爾電視機
public class ProductA1 extends AbstractProductA {

    public ProductA1(String nam) {
        name = nam;
    }
    @Override
    public void watch() {
        System.out.println(type+"-"+name);
    }
}
//電冰箱的具體產品--海爾冰箱V2
public class ProductB2 extends AbstractProductB {

    public ProductB2(String nam) {
        name = nam;
    }
    @Override
    public void use() {
        System.out.println(type+"=="+name);
    }
//具體工廠--第二代生產線
public class ConcreteFactory2 extends AbstractFactory {

    public ConcreteFactory2() {
        System.out.println("這是二代生產線");
    }

    @Override
    public AbstractProductA createProductA() {
        return new ProductA2("海爾電視 V2");
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB2("海爾冰箱 V2");
    }
//Main入口
 public static void main(String[] args){
    //第一代電器生產線
    AbstractFactory factory1 = new ConcreteFactory1();
    AbstractProductA productA1 = factory1.createProductA();
    AbstractProductB productB1 = factory1.createProductB();
    productA1.watch();
    productB1.use();
    //第二代電器生產線
    AbstractFactory factory2 = new ConcreteFactory2();
    AbstractProductA productA2 = factory2.createProductA();
    AbstractProductB productB2 = factory2.createProductB();
    productA2.watch();
    productB2.use();
}

//執行結果
這是一代生產線
電視機-海爾電視機
電冰箱=海爾電冰箱
這是二代生產線
電視機--海爾電視 V2
電冰箱==海爾冰箱 V2

未完待續…

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