設計模式之-降龍十八掌

MageByte發表於2019-11-23

本文程式碼較多且綜合了好幾種設計模式使用,建議收藏或者慢慢觀看。

本文將綜合抽象工廠、組合模式、裝飾器模式、介面卡模式、觀察者模式等模式運用在本例子中。對於不熟悉這些模式的讀者可以閱讀歷史文章學習加強自己的程式碼心法。完整程式碼在 github:https://github.com/UniqueDong/zero-design-patterns 對應的 com.zero.headfirst.verb 包目錄下。

介面卡模式

首先我們從製造一個螢幕模擬器開始,模擬鴨子叫,根據需求我們先定義 Quackable介面,然後分別定義MallarDuck、RedheadDuck、RubberDuck、DuckCall等不同的鴨子實現呱呱叫介面,這個時候有鴨子出現的地方也出現了鵝混在其中,我們就通過介面卡模式適配成鴨子。

/**
 * 鴨子呱呱叫介面
 */
public interface Quackable {
    /**
     * 呱呱叫
     */
    void quack();
}

/**
 * 鴨子叫玩具
 */
public class DuckCall implements Quackable {
    @Override
    public void quack() {
        System.out.println("鴨子模擬器叫...");
    }
}

public class MallarDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("標準綠頭鴨呱呱叫...");
    }
}

public class RedheadDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("紅頭鴨呱呱叫...");
    }
}

public class RubberDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("橡皮鴨吱吱叫...");
    }
}
複製程式碼

現在各種鴨子定義好了,我們定義鵝 Goose

/**
 * 天鵝,假裝鴨子在模擬器中出現
 */
public class Goose {
    public void honk() {
        System.out.println("天鵝叫聲,額");
    }
}複製程式碼

這個時候需要介面卡將鵝適配成鴨子,介面卡持有鵝的引用,鵝就是被適配物件,同時介面卡實現 Quackable介面,當呼叫 quack()的時候實際是委託呼叫了honk()方法。

/**
 * 將天鵝適配成鴨子
 */
public class GooseAdapter implements Quackable {
    /**
     * 持有被修飾的物件
     */
    private Goose goose;

    public GooseAdapter(Goose goose) {
        this.goose = goose;
    }

    @Override
    public void quack() {
        goose.honk();
    }
}複製程式碼

最後我們的螢幕模擬器登場,展示滿屏呱呱叫的鴨子與鵝。

public class DuckSimulation {
    public static void main(String[] args) {
        DuckSimulation simulation = new DuckSimulation();
        simulation.simulate();
    }

    /**
     * 模擬螢幕展示功能
     */
    private void simulate() {
        Quackable redheadDuck = new RedheadDuck();
        Quackable mallarDuck = new MallarDuck();
        Quackable rubberDuck = new RubberDuck();
        Quackable duckCall = new DuckCall();
        GooseAdapter gooseAdapter = new GooseAdapter(new Goose());
        simulate(redheadDuck);
        simulate(mallarDuck);
        simulate(rubberDuck);
        simulate(duckCall);
        simulate(gooseAdapter);
    }

    private void simulate(Quackable quackable) {
        quackable.quack();
    }

}
複製程式碼

裝飾器-統計鴨子叫的次數

現在繼續新增需求,在鴨子類不修改的情況下我們要統計叫的次數。我們建立一個裝飾者,通過把鴨子包裝進裝飾者物件,然後給鴨子新的功能(計算叫的次數)。裝飾器也要實現 Quackable介面,並且持有鴨子例項變數-被裝飾者,內部使用一個靜態變數儲存叫的次數,當 quack()被呼叫的時候我們就把呼叫委託給被裝飾的 Quackable物件,並且把叫的次數加 1。

/**
 * 裝飾器模式,發出叫聲的時候同時記錄次數.這樣我們就不必修改每個鴨子發出聲音的方法
 */
public class QuackCounter implements Quackable {

    private Quackable quack;

    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public QuackCounter(Quackable quack) {
        this.quack = quack;
    }

    @Override
    public void quack() {
        quack.quack();
        atomicInteger.incrementAndGet();
    }

    public static AtomicInteger getQuacks() {
        return atomicInteger;
    }
}複製程式碼

利用裝飾器模式-我們實現了不修改鴨子類卻又給鴨子新增了功能。接著我們需要修改螢幕模擬器,將需要統計叫聲的鴨子包裝在裝飾器中。

public class DuckSimulation {
    public static void main(String[] args) {
        DuckSimulation simulation = new DuckSimulation();
        simulation.simulate();
    }

    /**
     * 模擬螢幕展示功能
     */
    private void simulate() {
        // 使用裝飾器包裝鴨子
        Quackable redheadDuck = new QuackCounter(new RedheadDuck());
        Quackable mallarDuck = new QuackCounter(new MallarDuck());
        Quackable rubberDuck = new QuackCounter(new RubberDuck());
        Quackable duckCall = new QuackCounter(new DuckCall());
        //不想把天鵝的叫聲統計,所以不用裝飾器裝飾天鵝
        GooseAdapter gooseAdapter = new GooseAdapter(new Goose());
        System.out.println("使用裝飾器模式後,統計叫的次數,不包含天鵝");

        simulate(redheadDuck);
        simulate(mallarDuck);
        simulate(rubberDuck);
        simulate(duckCall);
        simulate(gooseAdapter);
        System.out.println("一共叫了 " + QuackCounter.getQuacks() + " 次");
    }

    private void simulate(Quackable quackable) {
        quackable.quack();
    }

}
複製程式碼

抽象工廠

寫到這裡我們已經用上了介面卡模式、裝飾器模式。有沒有覺得我們建立鴨子都是 new 出來的?為什麼不把建立鴨子的的程式集合集中在一個地方,換句話說就是將建立於修飾的部分包裝起來。就是我們接下來要說的:工廠模式。

我們定義一個工廠,生產各種不同型別的鴨子產品家族,所以我們使用抽象工廠模式。

首先從定義抽象工廠 AbstractDuckFactory開始,用於建立不同型別的鴨子家族。

/**
 * 抽象工廠模式,定義產品族
 */
public abstract class AbstractDuckFactory {
    public abstract Quackable createDuckCall();
    public abstract Quackable createMallarDuck();
    public abstract Quackable createRedheadDuck();
    public abstract Quackable createRubberDuck();
}複製程式碼

接著建立一個普通鴨子工廠繼承抽象工廠,該工廠建立沒有裝飾器裝飾的鴨子。螢幕模擬器並不知道實際的產品是什麼,只知道它實現了 Quackable介面。把建立細節丟給工廠,而不是直接 new 建立,這也是控制反轉的思想,在 Spring 框架中大量出現。

/**
 * 每個方法建立一種產品,一種特定種類的 Quackable
 */
public class DuckFactory extends AbstractDuckFactory {
    @Override
    public Quackable createDuckCall() {
        return new DuckCall();
    }

    @Override
    public Quackable createMallarDuck() {
        return new MallarDuck();
    }

    @Override
    public Quackable createRedheadDuck() {
        return new RedheadDuck();
    }

    @Override
    public Quackable createRubberDuck() {
        return new RubberDuck();
    }
}複製程式碼

現在我們要建立帶計數器叫聲功能的鴨子工廠DuckCountFactory,它持有DuckFactory 的一個例項用於建立普通鴨子並放進 QuackCounter裝飾器中從而得以建立帶計數器的鴨子。這裡其實也是用到了裝飾器模式。

/**
 * 叫聲計數器工廠:同時還結合了裝飾器模式,持有 工廠引用,包裝了duckFactory,從而增強了功能
 * 建立計數器鴨子
 */
public class DuckCountFactory extends AbstractDuckFactory {

    private AbstractDuckFactory duckFactory;

    public DuckCountFactory(DuckFactory duckFactory) {
        this.duckFactory = duckFactory;
    }

    @Override
    public Quackable createDuckCall() {
        return new QuackCounter(duckFactory.createDuckCall());
    }

    @Override
    public Quackable createMallarDuck() {
        return new QuackCounter(duckFactory.createMallarDuck());
    }

    @Override
    public Quackable createRedheadDuck() {
        return new QuackCounter(duckFactory.createRedheadDuck());
    }

    @Override
    public Quackable createRubberDuck() {
        return new QuackCounter(duckFactory.createRubberDuck());
    }
}複製程式碼

既然工廠我們定義好了,接下來就要修改螢幕模擬器程式碼運用工廠來產生鴨子。我們建立一個多型方法simulate(),此方法需要一個用來建立物件的工廠,傳入不同的工廠則生產不同的產品家族。

我們把原來直接 new 產生鴨子的程式碼改造成通過工廠建立,程式碼如下。

public class DuckSimulation {
    public static void main(String[] args) {
        DuckSimulation simulation = new DuckSimulation();
        AbstractDuckFactory duckCountFactory = new DuckCountFactory(new DuckFactory());
        simulation.simulate(duckCountFactory);
    }

    /**
     * 模擬螢幕展示功能
     */
    private void simulate(AbstractDuckFactory duckFactory) {
        Quackable redheadDuck = duckFactory.createRedheadDuck();
        Quackable mallarDuck = duckFactory.createMallarDuck();
        Quackable rubberDuck = duckFactory.createRubberDuck();
        Quackable duckCall = duckFactory.createDuckCall();
        //不想把天鵝的叫聲統計,所以不用裝飾器裝飾
        GooseAdapter gooseAdapter = new GooseAdapter(new Goose());
        System.out.println("使用裝飾器模式後,統計叫的次數,不包含天鵝,同時使用了工廠模式產生鴨子");

        simulate(redheadDuck);
        simulate(mallarDuck);
        simulate(rubberDuck);
        simulate(duckCall);
        simulate(gooseAdapter);
        System.out.println("一共叫了 " + QuackCounter.getQuacks() + " 次");
    }

    private void simulate(Quackable quackable) {
        quackable.quack();
    }

}
複製程式碼

組合模式-管理一群鴨子

需求方又來了,他們想控制不同鴨子群,把鴨子視為一個集合,為了滿足能夠管理不同鴨群的需求。

還記得麼?組合模式允許我們像對待單個物件一樣對待集合物件。所以利用組合模式管理一群Quackable再適合不過了。

首先,組合需要和葉子節點元素一樣實現相同的介面。這裡的「葉節點」就是Quackable

我們用一個 ArrayList儲存屬於這個組合的Quackable物件,用add()方法新增Quackable物件到組合中。因為鴨群也是Quackable,所以也具備quack()方法,該方法會對整個鴨群產生作用。

/**
 * 組合模式,管理一群鴨子: 對待單個物件一樣對待集合物件
 * 組合需要和葉子節點一樣實現相同的介面,這裡的葉子節點就是 Quackable
 */
public class Flock implements Quackable {

    private List<Quackable> quackers = new ArrayList<>();

    public void add(Quackable quackable) {
        quackers.add(quackable);
    }

    @Override
    public void quack() {
        for (Quackable quackable : quackers) {
            quackable.quack();
        }
    }
}複製程式碼

我們的組合好了,現在要修改模擬器實現鴨群定義與管理,和之前一樣。

  1. 首先建立所有的Quackable物件。
  2. 在建立不同的組合Flock鴨群,然後將對應的鴨子放入鴨群。
  3. 最後就能根據組合的鴨群分類測試鴨子滿屏飛以及控制不同的鴨群行為了。
public class DuckSimulation {
    public static void main(String[] args) {
        DuckSimulation simulation = new DuckSimulation();
        AbstractDuckFactory duckCountFactory = new DuckCountFactory(new DuckFactory());
        simulation.simulate(duckCountFactory);
    }

    /**
     * 模擬螢幕展示功能
     */
    private void simulate(AbstractDuckFactory duckFactory) {
        Quackable redheadDuck = duckFactory.createRedheadDuck();
        Quackable mallarDuck = duckFactory.createMallarDuck();
        Quackable rubberDuck = duckFactory.createRubberDuck();
        Quackable duckCall = duckFactory.createDuckCall();
        //不想把天鵝的叫聲統計,所以不用裝飾器裝飾
        GooseAdapter gooseAdapter = new GooseAdapter(new Goose());
        System.out.println("----使用裝飾器模式後,統計叫的次數,不包含天鵝,同時使用了工廠模式產生鴨子---");

        System.out.println("--使用組合模式管理鴨子群--");
        // 主要鴨子群
        Flock flockOfDucks = new Flock();

        flockOfDucks.add(redheadDuck);
        flockOfDucks.add(mallarDuck);
        flockOfDucks.add(rubberDuck);
        flockOfDucks.add(duckCall);
        flockOfDucks.add(gooseAdapter);
        // 綠頭鴨群
        Flock mallarFlock = new Flock();

        Quackable mallarDuck1 = duckFactory.createMallarDuck();
        Quackable mallarDuck2 = duckFactory.createMallarDuck();
        Quackable mallarDuck3 = duckFactory.createMallarDuck();
        Quackable mallarDuck4 = duckFactory.createMallarDuck();
        mallarFlock.add(mallarDuck1);
        mallarFlock.add(mallarDuck2);
        mallarFlock.add(mallarDuck3);
        mallarFlock.add(mallarDuck4);

        System.out.println("----主要鴨子群模擬器----");
        simulate(flockOfDucks);
        System.out.println("---綠頭鴨群模擬---");
        simulate(mallarFlock);
        System.out.println("一共叫了 " + QuackCounter.getQuacks() + " 次");
    }

    private void simulate(Quackable quackable) {
        quackable.quack();
    }

}
複製程式碼

觀察者模式-觀察特定鴨子叫

組合模式讓我們很好的管理鴨群,但是現在產品經理又有一個相反的需求:我們也需要追蹤個別鴨子,當它呱呱叫鵝時候我們能夠收到通知。

同學們是不是想到觀察和模式,當感興趣的某個事件發生的時候我們就收到通知。就像我們訂閱的公眾號傳送訊息,那麼就通知。

被觀察者QuackObservable

首先我們需要定義被觀察者角色,提供「註冊觀察者」、「移除觀察者」、「通知觀察者」方法。任何想被觀察的Quackable都要實現該介面,任何註冊到QuackObservable的觀察者QuackObserver都會收到呱呱叫通知,曬後我們會定義觀察者。

/**
 * 被觀察者:需要管理觀察者與通知觀察者
 */
public interface QuackObservable {
    /**
     * 註冊觀察者
     * @param observer
     */
    void registerObserver(QuackObserver observer);

    /**
     * 移除觀察者
     * @param observer
     */
    void removeObserver(QuackObserver observer);

    /**
     * 通知觀察者
     */
    void notifyObservers();
}
複製程式碼

現在我們需要把所有鴨子實現該介面成為被觀察者,從而使得我們可以觀察呱呱叫。所以我們乾脆讓Quackable來繼承QuackObservable介面。我們必須確認所有實現Quackable的具體類能夠扮演被觀察者角色。

/**
 * 呱呱叫介面,繼承被觀察者介面,讓所有實現了 Quackable 的實現類能夠扮演被觀察者
 */
public interface Quackable extends QuackObservable {
    /**
     * 呱呱叫
     */
    void quack();
}複製程式碼

ObservableDelegate輔助類

由於所有的鴨子都實現了Quackable介面,而該介面又繼承了QuackObservable被觀察者介面。所以每個鴨子類都需要實現註冊、通知、取消註冊的程式碼。我們不這麼幹,抽出一個輔助類ObservableDelegate封裝「註冊」、「通知」、「取消註冊」功能,然後和QuackObservable組合在一起,這樣只需要一份程式碼即可,QuackObservable的所有呼叫都委託到ObservableDelegate輔助類。

在構造方法中我們傳入QuackObservable,好讓觀察者知道是哪個物件在呱呱叫。

/**
 * 實現被觀察者介面,與 QuackObservable 組合在一起,這樣我們只需要一份程式碼,QuackObservable 的所有呼叫都委託給
 * Observable 輔助類
 */
public class ObservableDelegate implements QuackObservable {

    private List<QuackObserver> observerList = new ArrayList<>();

    private QuackObservable duck;

    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public ObservableDelegate(QuackObservable duck) {
        this.duck = duck;
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            observerList.add(observer);
        } finally {
            writeLock.unlock();
        }

    }

    @Override
    public void removeObserver(QuackObserver observer) {
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            observerList.remove(observer);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void notifyObservers() {
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        readLock.lock();
        try {
           //通知所有的觀察者
             observerList.forEach(item -> item.update(duck));
        } finally {
          readLock.unlock();
        }

    }
}
複製程式碼

輔助類實現了所有必要的功能,我們只要把它插進一個類就可以將工作委託到 ObservableDelegate。

接下來我們整合 ObservableDelegateQuackable使得 Quackable的註冊於通知都委託到輔助類。所以每個Quackable的實現類都應該持有ObservableDelegate的例項。接下來我們改造鴨子實現類。

public class MallarDuck implements Quackable {

    private QuackObservable observableDelegate;

    public MallarDuck() {
        this.observableDelegate = new ObservableDelegate(this);
    }

    @Override
    public void quack() {
        System.out.println("標準綠頭鴨呱呱叫...");
        //當呱呱叫的時候讓觀察者知道
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}


/**
 * 鴨子叫模擬器
 */
public class DuckCall implements Quackable {

    /**
     * 持有 QuackObservable 的引用,將 委託給輔助類實現
     */
    private QuackObservable observableDelegate;

    public DuckCall() {
        // 委託給觀察者輔助類
        this.observableDelegate = new ObservableDelegate(this);
    }

    @Override
    public void quack() {
        System.out.println("鴨子模擬器叫...");
        //當呱呱叫的時候讓觀察者知道
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}


public class RedheadDuck implements Quackable {

    private QuackObservable observableDelegate;

    public RedheadDuck() {
        this.observableDelegate = new ObservableDelegate(this);
    }
    @Override
    public void quack() {
        System.out.println("紅頭鴨呱呱叫...");
        //當呱呱叫的時候讓觀察者知道
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}


/**
 * 橡皮鴨
 */
public class RubberDuck implements Quackable {

    private QuackObservable observableDelegate;

    public RubberDuck() {
        this.observableDelegate = new ObservableDelegate(this);
    }
    @Override
    public void quack() {
        System.out.println("橡皮鴨吱吱叫...");//當呱呱叫的時候讓觀察者知道
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}
複製程式碼

還有我們的介面卡、以及鴨子叫計數器也是都把被觀察者核心程式碼功能呼叫委託給輔助類

/**
 * 將天鵝適配成鴨子
 */
public class GooseAdapter implements Quackable {
    /**
     * 持有被修飾的物件
     */
    private Goose goose;

    private QuackObservable observableDelegate;

    public GooseAdapter(Goose goose) {
        this.goose = goose;
        observableDelegate = new ObservableDelegate(this);
    }

    @Override
    public void quack() {
        goose.honk();
        //當呱呱叫的時候讓觀察者知道
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}


/**
 * 裝飾器模式,發出叫聲的時候同時記錄次數.這樣我們就不必修改每個鴨子發出聲音的方法
 */
public class QuackCounter implements Quackable {

    private Quackable quack;

    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public QuackCounter(Quackable quack) {
        this.quack = quack;
    }

    @Override
    public void quack() {
        quack.quack();
        atomicInteger.incrementAndGet();
    }

    public static AtomicInteger getQuacks() {
        return atomicInteger;
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        quack.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        quack.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        quack.notifyObservers();
    }
}
複製程式碼

以及之前定義的鴨群,假如我們也想觀察鴨群的叫通知,所以鴨群也要變成被觀察者,同時將呼叫委託給

ObservableDelegate

public class Flock implements Quackable {

    private List<Quackable> quackers = new ArrayList<>();

    private QuackObservable observableDelegate;

    public void add(Quackable quackable) {
        quackers.add(quackable);
        observableDelegate = new ObservableDelegate(this);
    }

    @Override
    public void quack() {
        for (Quackable quackable : quackers) {
            quackable.quack();
        }
        notifyObservers();
    }

    @Override
    public void registerObserver(QuackObserver observer) {
        quackers.forEach(item -> item.registerObserver(observer));
        observableDelegate.registerObserver(observer);
    }

    @Override
    public void removeObserver(QuackObserver observer) {
        quackers.forEach(item -> item.removeObserver(observer));
        observableDelegate.removeObserver(observer);
    }

    @Override
    public void notifyObservers() {
        observableDelegate.notifyObservers();
    }
}
複製程式碼

觀察者-QuackObserver

最後、我們要準備觀察了,現在需要一些觀察者。首先就從定義QuackObserver介面開始,它只有一個方法update(QuackObservable duck),傳入的就是呱呱叫物件。

/**
 * 鴨子觀察者,當被通知的時候執行update
 */
public interface QuackObserver {
    /**
     * @param duck 正在呱呱叫的物件
     */
    void update(QuackObservable duck);
}複製程式碼

現在我們來實現一個觀察者觀察呱呱叫,當收到通知的時候我們就列印下是誰叫的。

/**
 * 呱呱叫觀察者,觀察感興趣的鴨子
 */
public class Quackologist implements QuackObserver {
    @Override
    public void update(QuackObservable duck) {
        System.out.println("Quackologist: " + duck + " just quacked。");
    }
}複製程式碼

螢幕模擬器

大功告成,我們只要在模擬器上建立觀察者,並把觀察者註冊到感興趣的被觀察者中就實現了。

public class DuckSimulation {
    public static void main(String[] args) {
        DuckSimulation simulation = new DuckSimulation();
        AbstractDuckFactory duckCountFactory = new DuckCountFactory(new DuckFactory());
        simulation.simulate(duckCountFactory);
    }

    /**
     * 模擬螢幕展示功能
     */
    private void simulate(AbstractDuckFactory duckFactory) {
        Quackable redheadDuck = duckFactory.createRedheadDuck();
        Quackable mallarDuck = duckFactory.createMallarDuck();
        Quackable rubberDuck = duckFactory.createRubberDuck();
        Quackable duckCall = duckFactory.createDuckCall();
        //不想把天鵝的叫聲統計,所以不用裝飾器裝飾
        GooseAdapter gooseAdapter = new GooseAdapter(new Goose());
        System.out.println("----使用裝飾器模式後,統計叫的次數,不包含天鵝,同時使用了工廠模式產生鴨子---");

        System.out.println("--使用組合模式管理鴨子群--");
        // 主要鴨子群
        Flock flockOfDucks = new Flock();

        flockOfDucks.add(redheadDuck);
        flockOfDucks.add(mallarDuck);
        flockOfDucks.add(rubberDuck);
        flockOfDucks.add(duckCall);
        flockOfDucks.add(gooseAdapter);
        // 綠頭鴨群
        Flock mallarFlock = new Flock();

        Quackable mallarDuck1 = duckFactory.createMallarDuck();
        Quackable mallarDuck2 = duckFactory.createMallarDuck();
        Quackable mallarDuck3 = duckFactory.createMallarDuck();
        Quackable mallarDuck4 = duckFactory.createMallarDuck();
        mallarFlock.add(mallarDuck1);
        mallarFlock.add(mallarDuck2);
        mallarFlock.add(mallarDuck3);
        mallarFlock.add(mallarDuck4);

        //觀察者模式觀察指定鴨子的叫,flockOfDucks 被觀察者註冊了 一個觀察者
        System.out.println("====觀察者模式 start===");
        flockOfDucks.registerObserver(new Quackologist());

        System.out.println("----主要鴨子群模擬器----");
        simulate(flockOfDucks);
        System.out.println("---綠頭鴨群模擬---");
        simulate(mallarFlock);
        System.out.println("一共叫了 " + QuackCounter.getQuacks() + " 次");
    }

    private void simulate(Quackable quackable) {
        quackable.quack();
    }

}
複製程式碼

主要的改動就是

flockOfDucks.registerObserver(new Quackologist());建立觀察者,並且觀察 flockOfDucks這個鴨群的呱呱叫情況。

總結

最後我們用這個例子主要是開啟讀者的思維,遇到不同場景根據設計模式的特性便可以將問題迎刃而解,而且程式碼也變得優雅、高內聚低耦合,程式碼也得到了複用。

從一開始的鵝適配-「介面卡模式」,再到為鴨子叫計數功能增強使用「裝飾器模式」,接著建立很多鴨子。就想到了建立型模式「工廠模式」,為了管理鴨群-則想到了「組合模式」,最後想要觀察特定鴨子叫,便很快的使用了觀察者模式、同時還運用了「委託模式」使得註冊觀察的程式碼複用。

若對使用到的設計模式不熟悉可以閱讀歷史文章瞭解,歡迎大家提出意見以及指正。

關注公眾號JavaStorm,更多幹貨

JavaStorm

相關文章