[Java]24種設計模式

24種設計模式

第1章 簡單工廠

1.簡單工廠的定義
提供一個創建對象實例的功能,而無須關心其具體實現。被創建實例的類型可以是接口、抽象類,也可以是具體的類。

簡單工廠的結構如圖所示。
在這裏插入圖片描述
■ Api:定義客戶所需要的功能接口。

■ Impl:具體實現Api的實現類,可能會有多個。

■ Factory:工廠,選擇合適的實現類來創建Api接口對象。

■ Client:客戶端,通過Factory來獲取Api接口對象,然後面向Api接口編程。

2.簡單工廠示例代碼

public class Client {
    public static void main(String[] args) {
        SimpleFactory factory = new SimpleFactory();
        GoodApi goodApi = factory.createGood(1);
        goodApi.operation();
    }
}
interface GoodApi{
    void operation();
}

class GoodA implements  GoodApi{
    @Override
    public void operation() {
        System.out.println("GoodA");
    }
}

class GoodB implements  GoodApi{
    @Override
    public void operation() {
        System.out.println("GoodB");
    }
}

class SimpleFactory{
    public GoodApi createGood(int type){
        GoodApi goodApi = null;
        if(type==0){
            goodApi = new GoodA();
        }else if(type ==1 ){
            goodApi = new GoodB();
        }
        return goodApi;
    }
}

3.靜態工廠
使用簡單工廠的時候,通常不用創建簡單工廠類的類實例,沒有創建實例的必要。因此可以把簡單工廠類實現成一個工具類,直接使用靜態方法就可以了。也就是說簡單工廠的方法通常是靜態的,所以也被稱爲靜態工廠。如果要防止客戶端無謂地創造簡單工廠實例,還可以把簡單工廠的構造方法私有化了。

4.簡單工廠命名的建議
■ 類名稱建議爲“模塊名稱+Factory”。比如,用戶模塊的工廠就稱爲UserFactory。

■ 方法名稱通常爲“get+接口名稱”或者是“create+接口名稱”。比如,有一個接口名稱爲UserEbi,那麼方法名稱通常爲getUserEbi或者是createUserEbi。

5. 可配置的簡單工廠
使用配置文件,當有了新的實現類後,只要在配置文件裏面配置上新的實現類即可。在簡單工廠的方法裏面可以使用反射,當然也可以使用IoC/DI(控制反轉/依賴注入)來實現。

6 簡單工廠的優缺點
簡單工廠有以下優點。
■ 幫助封裝
簡單工廠雖然很簡單,但是非常友好地幫助我們實現了組件的封裝,然後讓組件外部能真正面向接口編程。
■ 解耦
通過簡單工廠,實現了客戶端和具體實現類的解耦。
如同上面的例子,客戶端根本就不知道具體是由誰來實現,
也不知道具體是如何實現的,客戶端只是通過工廠獲取它需要的接口對象。

簡單工廠有以下缺點。
■ 可能增加客戶端的複雜度
如果通過客戶端的參數來選擇具體的實現類,那麼就必須讓客戶端能理解各個參數所代表的具體功能和含義,
這樣會增加客戶端使用的難度,也部分暴露了內部實現,這種情況可以選用可配置的方式來實現。
■ 不方便擴展子工廠
私有化簡單工廠的構造方法,使用靜態方法來創建接口,也就不能通過寫簡單工廠類的子類來改變創建接口的方法的行爲了。
不過,通常情況下是不需要爲簡單工廠創建子類的。
7. 簡單工廠的本質
簡單工廠的本質是:選擇實現。
簡單工廠的目的在於爲客戶端來選擇相應的實現,從而使得客戶端和實現之間解耦。

第2章 外觀模式(Facade)

1.外觀模式的定義
外觀模式就是通過引入這麼一個外觀類,在這個類裏面定義客戶端想要的簡單的方法,然後在這些方法的實現裏面,由外觀類再去分別調用內部的多個模塊來實現功能,從而讓客戶端變得簡單。這樣一來,客戶端就只需要和外觀類交互就可以了。
通俗講就是一個大型系統對外有衆多接口,不同的接口屬於不同的服務。這對於另一個與其進行交互的系統來管理這麼多接口是糟糕的事!故facade模式講系統所有的接口都彙總到一個接口裏(稱之爲門面),與其交互的系統只需要管理這一個接口就可以。
在這裏插入圖片描述

2. 外觀模式示例代碼
在這裏插入圖片描述

public class FacadeDemo {
    static class Client{
        public static void main(String[] args) {
            Facade.opreation();
        }
    }
}

interface ModelA{
    void add();
}

interface ModelB{
    void delete();
}

interface ModelC{
    void update();
}

class ModelAImpl implements ModelA{
    @Override
    public void add() {
        System.out.println("modelA add()");
    }
}

class ModelBImpl implements ModelB{
    @Override
    public void delete() {
        System.out.println("modelA delete()");
    }
}

class ModelCImpl implements ModelC{
    @Override
    public void update() {
        System.out.println("modelC update()");
    }
}

class Facade{
    public static void opreation(){
        ModelA modelA = new ModelAImpl();
        modelA.add();

        ModelB modelB = new ModelBImpl();
        modelB.delete();

        ModelC modelC = new ModelCImpl();
        modelC.update();
    }
}

3.外觀模式的目的
外觀模式的目的不是給子系統添加新的功能接口,而是爲了讓外部減少與子系統內多個模塊的交互,鬆散耦合,從而讓外部能夠更簡單地使用子系統。

4. 外觀模式的優缺點
外觀模式有如下優點:

■ 鬆散耦合
外觀模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護。
■ 簡單易用
外觀模式讓子系統更加易用,客戶端不再需要了解子系統內部的實現,也不需要跟衆多子系統內部的模塊進行交互,
只需要跟外觀交互就可以了,相當於外觀類爲外部客戶端使用子系統提供了一站式服務。
■ 更好地劃分訪問的層次
通過合理使用Facade,可以幫助我們更好地劃分訪問的層次。
有些方法是對系統外的,有些方法是系統內部使用的。
把需要暴露給外部的功能集中到外觀中,這樣既方便客戶端使用,也很好地隱藏了內部的細節。

外觀模式有如下缺點:

過多的或者是不太合理的Facade也容易讓人迷惑。到底是調用Facade好呢,還是直接調用模塊好。

5.外觀模式的本質
外觀模式的本質是:封裝交互,簡化調用。

Facade封裝了子系統外部和子系統內部多個模塊的交互過程,從而簡化了外部的調用。通過外觀,子系統爲外部提供一些高層的接口,以方便它們的使用。

6.何時選用外觀模式
建議在如下情況時選用外觀模式。

■ 如果你希望爲一個複雜的子系統提供一個簡單接口的時候,可以考慮使用外觀模式。
使用外觀對象來實現大部分客戶需要的功能,從而簡化客戶的使用。

■ 如果想要讓客戶程序和抽象類的實現部分鬆散耦合,可以考慮使用外觀模式,
使用外觀對象來將這個子系統與它的客戶分離開來,從而提高子系統的獨立性和可移植性。

■ 如果構建多層結構的系統,可以考慮使用外觀模式,使用外觀對象作爲每層的入口,
這樣可以簡化層間調用,也可以鬆散層次之間的依賴關係。

第3章 適配器模式(Adapter)

1.適配器模式的定義
將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。

2. 適配器模式的結構和說明

在這裏插入圖片描述
■ Client:客戶端,調用自己需要的領域接口Target。

■ Target:定義客戶端需要的跟特定領域相關的接口。

■ Adaptee:已經存在的接口,通常能滿足客戶端的功能要求,但是接口與客戶端要求的特定領域接口不一致,需要被適配。

■ Adapter:適配器,把Adaptee適配成爲Client需要的Target。

3.示例代碼

public class AdapterDemo {
    static class Client{
        public static void main(String[] args) {
            Adaptee adaptee = new Adaptee();

            Adapter adapter = new Adapter(adaptee);
            adapter.request();
        }
    }
}

interface Target{
    void request();
}

class Adaptee{
    public void existingRequest(){
        System.out.println("已有接口");
    }
}

class Adapter implements Target{
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        System.out.println("適配現有接口");
        adaptee.existingRequest();
    }
}

4. 適配器模式的優缺點
適配器模式有如下優點。
■ 更好的複用性
如果功能是已經有了的,只是接口不兼容,那麼通過適配器模式就可以讓這些功能得到更好的複用。
■ 更好的可擴展性
在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。
適配器模式有如下缺點。
■ 過多地使用適配器,會讓系統非常零亂,不容易整體進行把握。

5.適配器模式的本質
適配器模式的本質是:轉換匹配,複用功能

適配器通過轉換調用已有的實現,從而能把已有的實現匹配成需要的接口,使之能滿足客戶端的需要。也就是說轉換匹配是手段,而複用已有的功能纔是目的。

6.何時選用適配器模式
建議在以下情況中選用適配器模式。
■ 如果你想要使用一個已經存在的類,但是它的接口不符合你的需求,這種情況可以使用適配器模式,
來把已有的實現轉換成你需要的接口。

■ 如果你想創建一個可以複用的類,這個類可能和一些不兼容的類一起工作,這種情況可以使用適配器模式,
到時候需要什麼就適配什麼。

■ 如果你想使用一些已經存在的子類,但是不可能對每一個子類都進行適配,這種情況可以選用對象適配器,
直接適配這些子類的父類就可以了。

第4章 單例模式(Singleton)

1.單例模式的定義
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

2. 單例模式的結構和說明
在這裏插入圖片描述

Singleton:負責創建Singleton類自己的唯一實例,並提供一個getInstance的方法,讓外部來訪問這個類的唯一實例。

3. 單例模式示例代碼
在Java中,單例模式的實現又分爲兩種,一種稱爲懶漢式,一種稱爲餓漢式,其實就是在具體創建對象實例的處理上,有不同的實現方式。下面分別來看看這兩種實現方式的代碼示例。

public class SingletonDemo {
    public static void main(String[] args) {
        System.out.println("懶漢式:");
        SluggardSingleton sluggardSingleton = SluggardSingleton.getInstance();
        System.out.println("餓漢式");
        HungrySingleton hungrySingleton = HungrySingleton.getInstance();
    }
}
/**
 * 懶漢式
 */
class SluggardSingleton{
    private static SluggardSingleton sluggardSingleton = null ;

    /**
     * 私有化構造器,不能通過new創建
     */
    private SluggardSingleton() {
    }

    public static synchronized SluggardSingleton getInstance(){
        if(sluggardSingleton == null){
            sluggardSingleton = new SluggardSingleton();
        }
        return sluggardSingleton;
    }
}

/**
 * 餓漢式
 */

class HungrySingleton{
    private static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static synchronized HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

所謂餓漢式,既然餓,那麼在創建對象實例的時候就比較着急,餓了嘛,於是就在裝載類的時候就創建對象實例。

所謂懶漢式,既然是懶,那麼在創建對象實例的時候就不着急,會一直等到馬上要使用對象實例的時候纔會創建,懶人嘛,總是推託不開的時候纔去真正執行工作,因此在裝載對象的時候不創建對象實例。

4.單例模式的功能
單例模式是用來保證這個類在運行期間只會被創建一個類實例,另外,單例模式還提供了一個全局唯一訪問這個類實例的訪問點,就是getInstance方法。不管採用懶漢式還是餓漢式的實現方式,這個全局訪問點是一樣的。

對於單例模式而言,不管採用何種實現方式,它都是隻關心類實例的創建問題,並不關心具體的業務功能。

單例模式的懶漢式實現方式體現了延遲加載的思想。

單例模式的懶漢式實現還體現了緩存的思想,緩存也是實際開發中常見的功能。

5. 單例模式的優缺點
1.時間和空間

比較上面兩種寫法:懶漢式是典型的時間換空間,也就是每次獲取實例都會進行判斷,看是否需要創建實例,浪費判斷的時間。當然,如果一直沒有人使用的話,那就不會創建實例,則節約內存空間。

餓漢式是典型的空間換時間,當類裝載的時候就會創建類實例,不管你用不用,先創建出來,然後每次調用的時候,就不需要再判斷了,節省了運行時間。

2.線程安全
(1)從線程安全性上講,不加同步的懶漢式是線程不安全的。
(2)餓漢式是線程安全的,因爲虛擬機保證只會裝載一次,在裝載類的時候是不會發生併發的。

(4)雙重檢查加鎖
所謂雙重檢查加鎖機制,指的是:並不是每次進入getInstance方法都需要同步,而是先不同步,進入方法過後,先檢查實例是否存在,如果不存在才進入下面的同步塊,這是第一重檢查。進入同步塊過後,再次檢查實例是否存在,如果不存在,就在同步的情況下創建一個實例,這是第二重檢查。這樣一來,就只需要同步一次了,從而減少了多次在同步情況下進行判斷所浪費的時間。
雙重檢查加鎖機制的實現會使用一個關鍵字volatile,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內存,從而確保多個線程能正確的處理該變量。

這種實現方式既可以實現線程安全地創建實例,而又不會對性能造成太大的影響。它只是在第一次創建實例的時候同步,以後就不需要同步了,從而加快了運行速度。

由於volatile關鍵字可能會屏蔽掉虛擬機中一些必要的代碼優化,所以運行效率並不是很高。因此一般建議,沒有特別的需要,不要使用。也就是說,雖然可以使用“雙重檢查加鎖”機制來實現線程安全的單例,但並不建議大量採用,可以根據情況來選用。

6. 在Java中一種更好的單例實現方式
■  什麼是類級內部類?
簡單點說,類級內部類指的是,有static修飾的成員式內部類。如果沒有static修飾的成員式內部類被稱爲對象級內部類。

■  類級內部類相當於其外部類的static成分,它的對象與外部類對象間不存在依賴關係,因此可直接創建。而對象級內部類的實例,是綁定在外部對象實例中的。

■  類級內部類中,可以定義靜態的方法。在靜態方法中只能夠引用外部類中的靜態成員方法或者成員變量。

■  類級內部類相當於其外部類的成員,只有在第一次被使用的時候纔會被裝載。

在多線程開發中,爲了解決併發問題,主要是通過使用synchronized來加互斥鎖進行同步控制。但是在某些情況中,JVM已經隱含地爲您執行了同步,這些情況下就不用自己再來進行同步控制了。這些情況包括:

■  由靜態初始化器(在靜態字段上或static{}塊中的初始化器)初始化數據時

■  訪問final字段時

■  在創建線程之前創建對象時

■  線程可以看見它將要處理的對象時

6.1 解決方案的思路

要想很簡單地實現線程安全,可以採用靜態初始化器的方式,它可以由JVM來保證線程的安全性。

一種可行的方式就是採用類級內部類,在這個類級內部類裏面去創建對象實例。這樣一來,只要不使用到這個類級內部類,那就不會創建對象實例,從而同時實現延遲加載和線程安全。

示例代碼:

public class SingletonDemo {
    public static void main(String[] args) {
        System.out.println("類級內部類方式");
        SingletonDemo singletonDemo = SingletonDemo.getInstance();
    }

    private static class SingletonHolder{
        private static SingletonDemo singletonDemo = new SingletonDemo();
    }

    public static SingletonDemo getInstance(){
        return SingletonHolder.singletonDemo;
    }
}

當getInstance方法第一次被調用的時候,它第一次讀取SingletonHolder.instance,導致SingletonHolder類得到初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而創建Singleton的實例,由於是靜態的域,因此只會在虛擬機裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。
這個模式的優勢在於,getInstance方法並沒有被同步,並且只是執行一個域的訪問,因此延遲初始化並沒有增加任何訪問成本。

7.單例模式的本質
單例模式的本質:控制實例數目。

8.何時選用單例模式
建議在如下情況時,選用單例模式。
當需要控制一個類的實例只能有一個,而且客戶只能從一個全局訪問點訪問它時,可以選用單例模式,這些功能恰好是單例模式要解決的問題。

第5章 工廠方法模式 (Factory Method)

1.工廠方法(Factory Method)模式的定義
定義一個用於創建對象的接口,讓子類決定實例化哪一個類,使一個類的實例化延遲到其子類。

2. 工廠方法模式的結構和說明

在這裏插入圖片描述
■ Product:定義工廠方法所創建的對象的接口,也就是實際需要使用的對象的接口。

■ ConcreteProduct:具體的Product接口的實現對象。

■ Creator:創建器,聲明工廠方法,工廠方法通常會返回一個Product類型的實例對象,而且多是抽象方法。也可以在Creator裏面提供工廠方法的默認實現,讓工廠方法返回一個缺省的Product類型的實例對象。

■ ConcreteCreator:具體的創建器對象,覆蓋實現Creator定義的工廠方法,返回具體的Product實例。

3. 工廠方法模式示例代碼

public class FactoryMethodDemo {
    public static void main(String[] args) {
        Creater creater = new ConcreteCreaterA();
        Product productA = creater.factoryMethod();
        productA.printLog();

        creater = new ConcreteCreaterB();
        Product productB = creater.factoryMethod();
        productB.printLog();
    }
}
interface Product{
    void printLog();
}

class ConcreteProductA implements  Product{
    @Override
    public void printLog() {
        System.out.println("ConcreteProductA");
    }
}

class ConcreteProductB implements  Product{
    @Override
    public void printLog() {
        System.out.println("ConcreteProductB");
    }
}

abstract class Creater{
    protected abstract Product factoryMethod();

    public void someOpreations(){}
}

class ConcreteCreaterA extends Creater{
    @Override
    protected Product factoryMethod() {
        return new ConcreteProductA();
    }
}

class ConcreteCreaterB extends Creater{
    @Override
    protected Product factoryMethod() {
        return new ConcreteProductB();
    }
}

4. 參數化工廠方法
可以改造工廠方法,去除抽象工廠類,參考簡單工廠實現參數化工廠方法。

這是一種很常見的參數化工廠方法的實現方式,但是也還是有把參數化工廠方法實現成爲抽象的,這點要注意,並不是說參數化工廠方法就不能實現成爲抽象類了。只是一般情況下,參數化工廠方法,在父類都會提供默認的實現。

5. 工廠方法模式的優缺點
工廠方法模式的優點
■ 可以在不知具體實現的情況下編程
工廠方法模式可以讓你在實現功能的時候,如果需要某個產品對象,只需要使用產品的接口即可,而無需關心具體的實現。選擇具體實現的任務延遲到子類去完成。
■ 更容易擴展對象的新版本
工廠方法給子類提供了一個掛鉤,使得擴展新的對象版本變得非常容易。比如上面示例的參數化工廠方法實現中,擴展一個新的導出xml文件格式的實現,已有的代碼都不會改變,只要新加入一個子類來提供新的工廠方法實現,然後在客戶端使用這個新的子類即可。
提示
另外這裏提到的掛鉤,就是我們經常說的鉤子方法(hook),這個會在後面講模板方法模式的時候詳細點說明。
■ 連接平行的類層次
工廠方法除了創造產品對象外,在連接平行的類層次上也大顯身手。這個在前面已經詳細講述了。
工廠方法模式的缺點
■ 具體產品對象和工廠方法的耦合性。
在工廠方法模式中,工廠方法是需要創建產品對象的,也就是需要選擇具體的產品對象,並創建它們的實例,因此具體產品對象和工廠方法是耦合的。
6. 工廠方法模式的本質
工廠方法模式的本質:延遲到子類來選擇實現。

第6章 抽象工廠模式(Abstract Factory)

1. 抽象工廠模式的定義
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

2. 抽象工廠模式的結構和說明
在這裏插入圖片描述

Abstract Factory:抽象工廠,定義創建一系列產品對象的操作接口。

■ Concrete Factory:具體的工廠,實現抽象工廠定義的方法,具體實現一系列產品對象的創建。

■ Abstract Product:定義一類產品對象的接口。

■ Concrete Product:具體的產品實現對象,通常在具體工廠裏面,會選擇具體的產品實現對象,來創建符合抽象工廠定義的方法返回的產品類型的對象。

■ Client:客戶端,主要使用抽象工廠來獲取一系列所需要的產品對象,然後面向這些產品對象的接口編程,以實現需要的功能。

3. 抽象工廠模式示例代碼

public class AbstractFactoryMethodDemo {
    public static void main(String[] args) {
        AbstractFactoryMethod abstractFactoryMethod = new ConcreteFactoryMethodA1();
        ProductA productA = abstractFactoryMethod.createA();
        ProductB productB = abstractFactoryMethod.createB();
        productA.printAlog();
        productB.printBlog();

        abstractFactoryMethod = new ConcreteFactoryMethodB1();
        productA = abstractFactoryMethod.createA();
        productB = abstractFactoryMethod.createB();
        productA.printAlog();
        productB.printBlog();
    }
}

interface ProductA{
    void printAlog();
}
interface ProductB{
    void printBlog();
}

class ConcreteProductA1 implements ProductA{
    @Override
    public void printAlog() {
        System.out.println("A1");
    }
}
class ConcreteProductA2 implements ProductA{
    @Override
    public void printAlog() {
        System.out.println("A2");
    }
}
class ConcreteProductB1 implements ProductB{
    @Override
    public void printBlog() {
        System.out.println("B1");
    }
}
class ConcreteProductB2 implements ProductB{
    @Override
    public void printBlog() {
        System.out.println("B2");
    }
}

interface AbstractFactoryMethod{
    ProductA createA();
    ProductB createB();
}

class ConcreteFactoryMethodA1 implements AbstractFactoryMethod{
    @Override
    public ProductA createA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createB() {
        return new ConcreteProductB1();
    }
}

class ConcreteFactoryMethodB1 implements AbstractFactoryMethod{
    @Override
    public ProductA createA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createB() {
        return new ConcreteProductB2();
    }
}

4. 抽象工廠模式的優缺點
抽象工廠模式的優點
■ 分離接口和實現
客戶端使用抽象工廠來創建需要的對象,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的接口編程而已。也就是說,客戶端從具體的產品實現中解耦。
■ 使得切換產品簇變得容易
因爲一個具體的工廠實現代表的是一個產品簇,比如上面例子的Scheme1代表裝機方案一:Intel的CPU+技嘉的主板,如果要切換成爲Scheme2,那就變成了裝機方案二:AMD的CPU+微星的主板。
客戶端選用不同的工廠實現,就相當於是在切換不同的產品簇。

抽象工廠模式的缺點
■ 不太容易擴展新的產品
前面也提到這個問題了,如果需要給整個產品簇添加一個新的產品,那麼就需要修改抽象工廠,這樣就會導致修改所有的工廠實現類。在前面提供了一個可以擴展工廠的方式來解決這個問題,但是又不夠安全。如何選擇,則要根據實際應用來權衡。
■ 容易造成類層次複雜
在使用抽象工廠模式的時候,如果需要選擇的層次過多,那麼會造成整個類層次變得複雜。

5.抽象工廠模式的本質
抽象工廠模式的本質:選擇產品簇的實現。

工廠方法是選擇單個產品的實現,雖然一個類裏面可以有多個工廠方法,但是這些方法之間一般是沒有聯繫的,即使看起來像有聯繫。
但是抽象工廠着重的就是爲一個產品簇選擇實現,定義在抽象工廠裏面的方法通常是有聯繫的,它們都是產品的某一部分或者是相互依賴的。如果抽象工廠裏面只定義一個方法,直接創建產品,那麼就退化成爲工廠方法了。

6.何時選用抽象工廠模式
建議在以下情況中選用抽象工廠模式。

■ 如果希望一個系統獨立於它的產品的創建、組合和表示的時候。換句話說,希望一個系統只是知道產品的接口,而不關心實現的時候。

■ 如果一個系統要由多個產品系列中的一個來配置的時候。換句話說,就是可以動態地切換產品簇的時候。
■ 如果要強調一系列相關產品的接口,以便聯合使用它們的時候。

第7章 生成器模式(Builder)

1.生成器模式的定義
將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示

2. 生成器模式的結構和說明

■ Builder:生成器接口,定義創建一個Product對象所需的各個部件的操作。

■ ConcreteBuilder:具體的生成器實現,實現各個部件的創建,並負責組裝Product對象的各個部件,同時還提供一個讓用戶獲取組裝完成後的產品對象的方法。

■ Director:指導者,也被稱爲導向者,主要用來使用Builder接口,以一個統一的過程來構建所需要的Product對象。

■ Product:產品,表示被生成器構建的複雜對象,包含多個部件。

3. 生成器模式示例代碼

public class BuilderDemo {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();

        Director director = new Director(builder);
        director.construct();
    }
}
interface BuildProduct{
    void someMethod();
}
interface Builder{
    void buildPart();
}
class ConcreteBuilder implements Builder{
    private String buildResult = null;

    public String getBuildResult() {
        return buildResult;
    }

    @Override
    public void buildPart() {
        System.out.println("構建產品細節。。。");
    }
}

class Director{
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct(){
        builder.buildPart();
    }
}

4. 生成器模式的優點
■ 鬆散耦合

生成器模式可以用同一個構建算法構建出表現上完全不同的產品,實現產品構建和產品表現上的分離。生成器模式正是把產品構建的過程獨立出來,使它和具體產品的表現鬆散耦合,從而使得構建算法可以複用,而具體產品表現也可以靈活地、方便地擴展和切換。

■ 可以很容易地改變產品的內部表示

在生成器模式中,由於Builder對象只是提供接口給Director使用,那麼具體的部件創建和裝配方式是被Builder接口隱藏了的,Director並不知道這些具體的實現細節。這樣一來,要想改變產品的內部表示,只需要切換Builder的具體實現即可,不用管Director,因此變得很容易。

■ 更好的複用性

生成器模式很好地實現了構建算法和具體產品實現的分離。這樣一來,使得構建產品的算法可以複用。同樣的道理,具體產品的實現也可以複用,同一個產品的實現,可以配合不同的構建算法使用。

5.生成器模式的本質
生成器模式的本質:分離整體構建算法和部件構造。

構建一個複雜的對象,本來就有構建的過程,以及構建過程中具體的實現。生成器模式就是用來分離這兩個部分,從而使得程序結構更鬆散、擴展更容易、複用性更好,同時也會使得代碼更清晰,意圖更明確。

6.何時選用生成器模式
建議在以下情況中選用生成器模式。

■ 如果創建對象的算法,應該獨立於該對象的組成部分以及它們的裝配方式時。

■ 如果同一個構建過程有着不同的表示時。

第8章 原型模式(Prototype)

1.原型模式的定義
在這裏插入圖片描述
用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象。

  1. 原型模式的結構和說明

■ Prototype:聲明一個克隆自身的接口,用來約束想要克隆自己的類,要求它們都要實現這裏定義的克隆方法。

■ ConcretePrototype:實現Prototype接口的類,這些類真正實現了克隆自身的功能。

■ Client:使用原型的客戶端,首先要獲取到原型實例對象,然後通過原型實例克隆自身來創建新的對象實例。

3. 原型模式示例代碼

public class PrototypeDemo {
    public static void main(String[] args) {
        Prototype prototypeA = new PrototypeA();
        prototypeA.setName("tom");
        System.out.println(prototypeA.getName());

        Client client = new Client(prototypeA);
        client.someOperation();
    }
}

interface Prototype{
    String getName();
    void setName(String name);
    Prototype clone();
}

class PrototypeA implements Prototype{
    private String name ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Prototype clone() {
        Prototype prototypeA = new PrototypeA();
        return prototypeA;
    }
}

class PrototypeB implements Prototype{
    private String name ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Prototype clone() {
        Prototype prototypeB = new PrototypeA();
        return prototypeB;
    }
}
class Client{
    private Prototype prototype;

    public Client(Prototype prototype) {
        this.prototype = prototype;
    }

    public Prototype someOperation(){
        Prototype newPrototype = prototype.clone();
        newPrototype.setName("liom");
        System.out.println(newPrototype.getName());
        return newPrototype;
    }
}

4.原型模式的功能
原型模式的功能實際上包含兩個方面:

■ 一個是通過克隆來創建新的對象實例;

■ 另一個是爲克隆出來的新的對象實例複製原型實例屬性的值。

5. Java中的克隆方法
需要克隆功能的類,只需要實現java.lang.Cloneable接口,這個接口沒有需要實現的方法,是一個標識接口。

6. 淺度克隆和深度克隆
■ 淺度克隆:只負責克隆按值傳遞的數據(比如基本數據類型、String類型)。

■ 深度克隆:除了淺度克隆要克隆的值外,還負責克隆引用類型的數據,基本上就是被克隆實例所有的屬性數據都會被克隆出來。

7. 原型模式的優缺點
原型模式的優點:

■ 對客戶端隱藏具體的實現類型

原型模式的客戶端只知道原型接口的類型,並不知道具體的實現類型,從而減少了客戶端對這些具體實現類型的依賴。

■ 在運行時動態改變具體的實現類型

原型模式可以在運行期間,由客戶來註冊符合原型接口的實現類型,也可以動態地改變具體的實現類型,看起來接口沒有任何變化,但其實運行的已經是另外一個類實例了。因爲克隆一個原型就類似於實例化一個類。

原型模式的缺點

原型模式最大的缺點就在於每個原型的子類都必須實現clone的操作,尤其在包含引用類型的對象時,clone方法會比較麻煩,必須要能夠遞歸地讓所有的相關對象都要正確地實現克隆。

8.原型模式的本質
原型模式的本質:克隆生成對象。

克隆是手段,目的是生成新的對象實例。正是因爲原型的目的是爲了生成新的對象實例,原型模式通常是被歸類爲創建型的模式。

原型模式也可以用來解決“只知接口而不知實現的問題”,使用原型模式,可以出現一種獨特的“接口造接口”的景象,這在面向接口編程中很有用。同樣的功能也可以考慮使用工廠來實現。

另外,原型模式的重心還是在創建新的對象實例,至於創建出來的對象,其屬性的值是否一定要和原型對象屬性的值完全一樣,這個並沒有強制規定,只不過在目前大多數實現中,克隆出來的對象和原型對象的屬性值是一樣的。

也就是說,可以通過克隆來創造值不一樣的實例,但是對象類型必須一樣。可以有部分甚至是全部的屬性的值不一樣,可以有選擇性地克隆,就當是標準原型模式的一個變形使用吧。

9.何時選用原型模式
建議在以下情況時選用原型模式。

■ 如果一個系統想要獨立於它想要使用的對象時,可以使用原型模式,讓系統只面向接口編程,在系統需要新的對象的時候,可以通過克隆原型來得到。

■ 如果需要實例化的類是在運行時刻動態指定時,可以使用原型模式,通過克隆原型來得到需要的實例。

第9章 中介者模式(Mediator)

在這裏插入圖片描述
1.中介者模式的定義
用一箇中介對象來封裝一系列的對象交互。中介者使得各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。

  1. 中介者模式的結構和說明

■ Mediator:中介者接口。在裏面定義各個同事之間交互需要的方法,可以是公共的通信方法,比如changed方法,大家都用,也可以是小範圍的交互方法。

■ ConcreteMediator:具體中介者實現對象。它需要了解並維護各個同事對象,並負責具體的協調各同事對象的交互關係。

■ Colleague:同事類的定義,通常實現成爲抽象類,主要負責約束同事對象的類型,並實現一些具體同事類之間的公共功能,比如,每個具體同事類都應該知道中介者對象,也就是具體同事類都會持有中介者對象,都可以定義到這個類裏面。

■ ConcreteColleague:具體的同事類,實現自己的業務,在需要與其他同事通信的時候,就與持有的中介者通信,中介者會負責與其他的同事交互。

3. 中介者模式示例代碼

public class MediatorDemo {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        ColleagueA colleague = new ColleagueA(mediator);
        colleague.someOperation();
        ColleagueB colleagueB = new ColleagueB(mediator);
        colleagueB.someOperation();
    }
}
interface Mediator{
    void changed(Colleague colleague);
}
abstract class Colleague{

    private Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    public Mediator getMediator() {
        return mediator;
    }
}

class ColleagueA extends Colleague{
    public ColleagueA(Mediator mediator) {
        super(mediator);
    }

    public void someOperation(){
        getMediator().changed(this);

    }
}

class ColleagueB extends Colleague{
    public ColleagueB(Mediator mediator) {
        super(mediator);
    }

    public void someOperation(){
        getMediator().changed(this);

    }
}

class ConcreteMediator implements Mediator{
    @Override
    public void changed(Colleague colleague) {
        if(colleague instanceof ColleagueA){
            System.out.println("完成與A的交互操作");
        }else if (colleague instanceof  ColleagueB){
            System.out.println("完成與B的交互操作");
        }
    }
}

4. 中介者模式的優缺點
中介者模式的優點。
■ 鬆散耦合
中介者模式通過把多個同事對象之間的交互封裝到中介者對象裏面,從而使得同事對象之間鬆散耦合,
基本上可以做到互不依賴。
這樣一來,同事對象就可以獨立地變化和複用,而不再像以前那樣“牽一髮而動全身”了。
■ 集中控制交互
多個同事對象的交互,被封裝在中介者對象裏面集中管理,使得這些交互行爲發生變化的時候,
只需要修改中介者對象就可以了,當然如果是已經做好的系統,那就擴展中介者對象,
而各個同事類不需要做修改。
■ 多對多變成一對多
沒有使用中介者模式的時候,同事對象之間的關係通常是多對多的,引入中介者對象以後,
中介者對象和同事對象的關係通常變成了雙向的一對多,這會讓對象的關係更容易理解和實現。

中介者模式的缺點。
中介者模式的一個潛在缺點是,過度集中化。如果同事對象的交互非常多,而且比較複雜,
當這些複雜性全部集中到中介者的時候,會導致中介者對象變得十分複雜,而且難於管理和維護。

5.中介者模式的本質
中介者模式的本質:封裝交互。

中介者模式的目的,就是用來封裝多個對象的交互,這些交互的處理多在中介者對象裏面實現。因此中介對象的複雜程度,就取決於它封裝的交互的複雜程度。

6.何時選用中介者模式
建議在以下情況時選用中介者模式。

■ 如果一組對象之間的通信方式比較複雜,導致相互依賴、結構混亂,可以採用中介者模式,把這些對象相互的交互管理起來,各個對象都只需要和中介者交互,從而使得各個對象鬆散耦合,結構也更清晰易懂。

■ 如果一個對象引用很多的對象,並直接跟這些對象交互,導致難以複用該對象,可以採用中介者模式,把這個對象跟其他對象的交互封裝到中介者對象裏面,這個對象只需要和中介者對象交互就可以了。

第10章 代理模式(Proxy)

1.代理模式的定義
在這裏插入圖片描述
爲其他對象提供一種代理以控制對這個對象的訪問。

2. 代理模式的結構和說明

■ Proxy:代理對象,通常具有如下功能。
實現與具體的目標對象一樣的接口,這樣就可以使用代理來代替具體的目標對象。保存一個指向具體目標對象的引用,可以在需要的時候調用具體的目標對象。

可以控制對具體目標對象的訪問,並可以負責創建和刪除它。

■ Subject:目標接口,定義代理和具體目標對象的接口,這樣就可以在任何使用具體目標對象的地方使用代理對象。

■ RealSubject:具體的目標對象,真正實現目標接口要求的功能。

3. 代理模式示例代碼

public class ProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

interface Subject{
    void request();
}
class RealSubject implements Subject{
    @Override
    public void request() {
        System.out.println("realSubject");
    }
}
class Proxy implements Subject{
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {

        System.out.println("開始請求前做一些校驗操作。。。");

        System.out.println("校驗完成!");
        realSubject.request();

        System.out.println("請求結束做一些處理事宜。。。");
        System.out.println("處理完成");
    }
}

4.代理的分類
事實上代理又被分成多種,大致有如下一些:

■ 虛代理:根據需要來創建開銷很大的對象,該對象只有在需要的時候纔會被真正創建。

■ 遠程代理:用來在不同的地址空間上代表同一個對象,這個不同的地址空間可以是在本機,也可以在其他機器上。在Java裏面最典型的就是RMI技術。

■ copy-on-write代理:在客戶端操作的時候,只有對象確實改變了,纔會真的拷貝(或克隆)一個目標對象,算是虛代理的一個分支。

■ 保護代理:控制對原始對象的訪問,如果有需要,可以給不同的用戶提供不同的訪問權限,以控制他們對原始對象的訪問。

■ Cache代理:爲那些昂貴操作的結果提供臨時的存儲空間,以便多個客戶端可以共享這些結果。

■ 防火牆代理:保護對象不被惡意用戶訪問和操作。

■ 同步代理:使多個用戶能夠同時訪問目標對象而沒有衝突。

■ 智能指引:在訪問對象時執行一些附加操作,比如,對指向實際對象的引用計數、第一次引用一個持久對象時,將它裝入內存等。

  1. Java中的代理
    Java對代理模式提供了內建的支持,在java.lang.reflect包下面,提供了一個Proxy的類和一個InvocationHandler的接口。

6.代理模式的本質
代理模式的本質:控制對象訪問。

代理模式通過代理目標對象,把代理對象插入到客戶和目標對象之間,從而爲客戶和目標對象引入一定的間接性。正是這個間接性,給了代理對象很多的活動空間。代理對象可以在調用具體的目標對象前後,附加很多操作,從而實現新的功能或是擴展目標對象的功能。更狠的是,代理對象還可以不去創建和調用目標對象,也就是說,目標對象被完全代理掉了,或是被替換掉了。

從實現上看,代理模式主要是使用對象的組合和委託,尤其是在靜態代理的實現裏面,會看得更清楚。但是也可以採用對象繼承的方式來實現代理,這種實現方式在某些情況下,比使用對象組合還要來得簡單。

7.何時選用代理模式
建議在如下情況中選用代理模式。

■ 需要爲一個對象在不同的地址空間提供局部代表的時候,可以使用遠程代理。

■ 需要按照需要創建開銷很大的對象的時候,可以使用虛代理。

■ 需要控制對原始對象的訪問的時候,可以使用保護代理。

■ 需要在訪問對象執行一些附加操作的時候,可以使用智能指引代理。

第11章 觀察者模式(Observer)

1.觀察者模式的定義
在這裏插入圖片描述
定義對象間的一種一對多的依賴關係。當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。

  1. 觀察者模式的結構和說明

■ Subject:目標對象,通常具有如下功能。

◆ 一個目標可以被多個觀察者觀察。

◆ 目標提供對觀察者註冊和退訂的維護。

◆ 當目標的狀態發生變化時,目標負責通知所有註冊的、有效的觀察者。
■ Observer:定義觀察者的接口,提供目標通知時對應的更新方法,這個更新方法進行相應的業務處理,可以在這個方法裏面回調目標對象,以獲取目標對象的數據。

■ ConcreteSubject:具體的目標實現對象,用來維護目標狀態,當目標對象的狀態發生改變時,通知所有註冊的、有效的觀察者,讓觀察者執行相應的處理。

■ ConcreteObserver:觀察者的具體實現對象,用來接收目標的通知,並進行相應的後續處理,比如更新自身的狀態以保持和目標的相應狀態一致。

3. 使用觀察者模式實現示例

import java.util.ArrayList;
import java.util.List;

public class ObserverDemo {
    public static void main(String[] args) {
        ConcreateObserver concreateObserver = new ConcreateObserver();
        concreateObserver.setObserverState("202");
        System.out.println(concreateObserver.getObserverState());

        ConcreateSubject subjectA = new ConcreateSubject();
        subjectA.attach(concreateObserver);
        subjectA.setSubjectState("404");
        System.out.println(concreateObserver.getObserverState());

        subjectA.detach(concreateObserver);
        subjectA.setSubjectState("500");
        System.out.println(concreateObserver.getObserverState());
    }
}
interface Observer{
    void update(SubjectA subject);
}
class SubjectA{
    private List<Observer> observerList = new ArrayList<Observer>();

    //註冊
    public void attach(Observer observer){
        observerList.add(observer);
    }

    //註銷
    public void detach(Observer observer){
        observerList.remove(observer);
    }

    public void notifyObservers(){
        for(Observer observer : observerList){
            observer.update(this);
        }
    }
}

class ConcreateSubject extends SubjectA{
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;

        this.notifyObservers();
    }
}

class ConcreateObserver implements Observer{
    private String ObserverState;
    @Override
    public void update(SubjectA subject) {
        ObserverState = ((ConcreateSubject)subject).getSubjectState();
    }

    public String getObserverState() {
        return ObserverState;
    }

    public void setObserverState(String observerState) {
        ObserverState = observerState;
    }
}
  1. 推模型和拉模型
    在觀察者模式的實現中,又分爲推模型和拉模型兩種方式。

■ 推模型

目標對象主動向觀察者推送目標的詳細信息,不管觀察者是否需要,推送的信息通常是目標對象的全部或部分數據,相當於是在廣播通信。

■ 拉模型

目標對象在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到目標對象中獲取,相當於是觀察者從目標對象中拉數據。一般這種模型的實現中,會把目標對象自身通過update方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。

5. 觀察者模式的優缺點
觀察者模式具有以下優點。

■ 觀察者模式實現了觀察者和目標之間的抽象耦合

原本目標對象在狀態發生改變的時候,需要直接調用所有的觀察者對象,但是抽象出觀察者接口以後,目標和觀察者就只是在抽象層面上耦合了,也就是說目標只是知道觀察者接口,並不知道具體的觀察者的類,從而實現目標類和具體的觀察者類之間解耦。

■ 觀察者模式實現了動態聯動

所謂聯動,就是做一個操作會引起其他相關的操作。由於觀察者模式對觀察者註冊實行管理,那就可以在運行期間,通過動態地控制註冊的觀察者,來控制某個動作的聯動範圍,從而實現動態聯動。

■ 觀察者模式支持廣播通信

由於目標發送通知給觀察者是面向所有註冊的觀察者,所以每次目標通知的信息就要對所有註冊的觀察者進行廣播。當然,也可以通過在目標上添加新的功能來限制廣播的範圍。

在廣播通信的時候要注意一個問題,就是相互廣播造成死循環的問題。比如A和B兩個對象互爲觀察者和目標對象,A對象發生狀態變化,然後A來廣播信息,B對象接收到通知後,在處理過程中,使得B對象的狀態也發生了改變,然後B來廣播信息,然後A對象接到通知後,又觸發廣播信息……,如此A引起B變化,B又引起A變化,從而一直相互廣播信息,就造成死循環。

觀察者模式的缺點是:

■ 可能會引起無謂的操作

由於觀察者模式每次都是廣播通信,不管觀察者需不需要,每個觀察者都會被調用update方法,如果觀察者不需要執行相應處理,那麼這次操作就浪費了。其實浪費了還好,最怕引起誤更新,那就麻煩了,比如,本應該在執行這次狀態更新前把某個觀察者刪除掉,這樣通知的時候就沒有這個觀察者了,但是現在忘掉了,那麼就會引起誤操作。

  1. 觀察者模式的本質
    觀察者模式的本質:觸發聯動。

當修改目標對象的狀態的時候,就會觸發相應的通知,然後會循環調用所有註冊的觀察者對象的相應方法,其實就相當於聯動調用這些觀察者的方法。

而且這個聯動還是動態的,可以通過註冊和取消註冊來控制觀察者,因而可以在程序運行期間,通過動態地控制觀察者,來變相地實現添加和刪除某些功能處理,這些功能就是觀察者在update的時候執行的功能。

同時目標對象和觀察者對象的解耦,又保證了無論觀察者發生怎樣的變化,目標對象總是能夠正確地聯動過來。

7.何時選用觀察者模式
建議在以下情況中選用觀察者模式。

■ 當一個抽象模型有兩個方面,其中一個方面的操作依賴於另一個方面的狀態變化,那麼就可以選用觀察者模式,將這兩者封裝成觀察者和目標對象,當目標對象變化的時候,依賴於它的觀察者對象也會發生相應的變化。這樣就把抽象模型的這兩個方面分離開了,使得它們可以獨立地改變和複用。

■ 如果在更改一個對象的時候,需要同時連帶改變其他的對象,而且不知道究竟應該有多少對象需要被連帶改變,這種情況可以選用觀察者模式,被更改的那一個對象很明顯就相當於是目標對象,而需要連帶修改的多個其他對象,就作爲多個觀察者對象了。

■ 當一個對象必須通知其他的對象,但是你又希望這個對象和其他被它通知的對象是鬆散耦合的。也就是說這個對象其實不想知道具體被通知的對象。這種情況可以選用觀察者模式,這個對象就相當於是目標對象,而被它通知的對象就是觀察者對象了。

第12章 命令模式(Command)

1.命令模式的定義
在這裏插入圖片描述
將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。

2. 命令模式的結構和說明

■ Command:定義命令的接口,聲明執行的方法。

■ ConcreteCommand:命令接口實現對象,是“虛”的實現;通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。

■ Receiver:接收者,真正執行命令的對象。任何類都可能成爲一個接收者,只要它能夠實現命令要求實現的相應功能。

■ Invoker:要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令對象的入口。

■ Client:創建具體的命令對象,並且設置命令對象的接收者。注意這個不是我們常規意義上的客戶端,而是在組裝命令對象和接收者,或許,把這個Client稱爲裝配者會更好理解,因爲真正使用命令的客戶端是從Invoker來觸發執行。

3. 命令模式示例代碼

public class CommandDemo {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreateCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.runCommand();
    }
}
interface Command{
    void execute();
}
class Receiver{
    public void action(){
        System.out.println("do something...");
    }
}
class ConcreateCommand implements Command{
    //命令自身狀態
    private String state;

    private Receiver receiver;

    public ConcreateCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }
}
class Invoker{
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void runCommand(){
        command.execute();
    }
}
  1. 命令模式的優點
    ■ 更鬆散的耦合
    命令模式使得發起命令的對象——客戶端,和具體實現命令的對象——接收者對象完全解耦,也就是說發起命令的對象完全不知道具體實現對象是誰,也不知道如何實現。

■ 更動態的控制
命令模式把請求封裝起來,可以動態地對它進行參數化、隊列化和日誌化等操作,從而使得系統更靈活。

■ 很自然的複合命令
命令模式中的命令對象能夠很容易地組合成複合命令,也就是前面講的宏命令,從而使系統操作更簡單,功能更強大。

■ 更好的擴展性
由於發起命令的對象和具體的實現完全解耦,因此擴展新的命令就很容易,只需要實現新的命令對象,然後在裝配的時候,把具體的實現對象設置到命令對象中,然後就可以使用這個命令對象,已有的實現完全不用變化。

5.命令模式的本質
命令模式的本質:封裝請求。

6.何時選用命令模式
建議在以下情況時選用命令模式。

■ 如果需要抽象出需要執行的動作,並參數化這些對象,可以選用命令模式。
將這些需要執行的動作抽象成爲命令,然後實現命令的參數化配置。

■ 如果需要在不同的時刻指定、排列和執行請求,可以選用命令模式。
將這些請求封裝成爲命令對象,然後實現將請求隊列化。

■ 如果需要支持取消操作,可以選用命令模式,通過管理命令對象,能很容易地實現命令的恢復和重做功能。

■ 如果需要支持當系統崩潰時,能將系統的操作功能重新執行一遍,可以選用命令模式。
將這些操作功能的請求封裝成命令對象,然後實現日誌命令,就可以在系統恢復以後,通過日誌獲取命令列表,從而重新執行一遍功能。

■ 在需要事務的系統中,可以選用命令模式。命令模式提供了對事務進行建模的方法。命令模式有一個別名就是Transaction。

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