個人Java設計模式-中介者模式

本篇文章已受權微信公衆號 guolin_blog (郭霖)獨家發佈git

小時候鍾愛戰爭片,《地道戰》、《雞毛信》、《鐵道游擊隊》一系列的老電影,咦~想起都激動得起雞皮疙瘩。不過以爲特別逗的是,電影裏面總會有「這裏是xxx,咱們被包圍了,請求支援請求支援」這麼一句臺詞。github

來分析一下這句臺詞怎麼來的。假設有N多個戰區,戰區的分佈錯綜複雜,不少時候一個戰區的丟失會影響整個戰爭局勢。因此這就得要有一個司令部指揮和協調各個戰區,而一旦戰區被攻打,報告司令部請求支援,司令部則調度其餘戰區進行協助。設計模式

那在咱們的程序設計中有沒有這樣的模式?有的,中介者模式應運而生,目的就是處理這樣的情景問題。微信

1、中介者模式

定義

  中介者封裝一系列對象相互做用,使得這些對象耦合鬆散,而且能夠獨立的改變它們之間的交互。dom

UML

中介者模式UML圖

中介者模式涉及到的角色有四個:優化

- 抽象中介者角色:抽象中介者角色定義統一的接口,以及一個或者多個事件方法,用於各同事角色之間的通訊。this

- 具體中介者角色:實現了抽象中介者所聲明的事件方法,協調各同事類之間的行爲,持有全部同事類對象的引用。spa

- 抽象同事類角色:定義了抽象同事類,持有抽象中介者對象的引用。設計

- 具體同事類角色:繼承抽象同事類,實現本身業務,經過中介者跟其餘同事類進行通訊。code

2、中介者模式實戰

假設這樣的一個情景。有A、B、C三個戰區,A被敵方攻打,請求B支援。可是此時B也被敵方攻打,因此A繼續向C請求支援,這麼巧C此時正在支援B。情景比較簡單,咱們的例子也圍繞着這個情景來展開,首先來看不使用中介者模式是怎麼實現的。

A戰區代碼以下:

public class SituationA {
    // 請求支援
    public void requestSupport(String situation) {
        System.out.println(getClass().getSimpleName() + ":這裏是A戰區,如今被敵方攻打,請求" + situation + "支援");
    }
}

SituationA定義了請求支援的方法,向其餘戰區請求支援。再來看B戰區的代碼定義:

public class SituationB {
    // 請求支援
    public void requestSupport(String situation) {
        System.out.println(getClass().getSimpleName() + ":這裏是B戰區,如今被敵方攻打,請求" + situation + "支援");
    }

    // 是否支援
    public void support(boolean isSupport) {
        if (isSupport) {
            System.out.println(getClass().getSimpleName() + ":Copy that,還有五秒鐘到達戰場");
        } else {
            System.out.println(getClass().getSimpleName() + ":支援你妹,我也正在被攻打");
        }
    }
}

SituationB也定義了請求支援的方法,還多了根據isSupport是否支援其餘戰區的方法。還有SituationC,SituationC和SituationB代碼差很少,直接貼出來了,很少作解釋。

public class SituationC {
    // 請求支援
    public void requestSupport(String situation) {
        System.out.println(getClass().getSimpleName() + ":這裏是B戰區,如今被敵方攻打,請求" + situation + "支援");
    }

    // 是否支援
    public void support(boolean isSupport) {
        if (isSupport) {
            System.out.println(getClass().getSimpleName() + ":Copy that,還有五秒鐘到達戰場");
        } else {
            System.out.println(getClass().getSimpleName() + ":很差意思,來遲一步了,正在前往別的戰區支援");
        }
    }
}

OK,三個類都定義好了,咱們根據情景看看客戶端是怎樣運行的,代碼以下:

public class Client {
    public static void main(String[] args) {
        System.out.println("-------A被攻打,請求B支援--------");
        SituationA situationA = new SituationA();
        situationA.requestSupport("B");
        System.out.println("-------B也正在被攻打--------");
        SituationB situationB = new SituationB();
        situationB.support(false);
        System.out.println("-------A又向C請求支援--------");
        situationA.requestSupport("C");
        System.out.println("-------C很忙--------");
        SituationC situationC = new SituationC();
        situationC.support(false);
    }
}

運行結果以下:

-------A被攻打,請求B支援--------
SituationA:這裏是A戰區,如今被敵方攻打,請求B支援
-------B也正在被攻打--------
SituationB:支援你妹,我也正在被攻打
-------A又向C請求支援--------
SituationA:這裏是A戰區,如今被敵方攻打,請求C支援
-------C很忙--------
SituationC:很差意思,來遲一步了,正在前往別的戰區支援

回到咱們的場景當中,A、B、C是相互兩兩關聯的,而且關聯的兩個類與其餘類是不能協調通訊。所以,在實際中,戰區類增多,它們之間的耦合度越高,這樣首先會形成當一個類修改了,其餘類也要跟着須要修改,而後就是多個類之間的通訊變得複雜混亂。就跟咱們上面列子同樣,A、B、C相互支援,A並不知道C已經支援B了。由於這些緣由,在代碼設計中加入中介者角色,每一個類都通過中介者進行溝通和協調。

下面來看中介者模式的實現,首先定義抽象中介者角色類,代碼以下:

public abstract class Mediator {
    protected SituationA situationA;
    protected SituationB situationB;
    protected SituationC situationC;

    Mediator() {
        situationA = new SituationA(this);
        situationB = new SituationB(this);
        situationC = new SituationC(this);
    }

    /**
     * 事件的業務流程處理
     *
     * @param method
     */
    public abstract void execute(String method);
}

抽象中介者類主要定義了同事類的事件業務流程方法,而且持有每個具體同事類的引用,再來看具體中介者的實現:

public class Command extends Mediator {

    public void execute(String method) {
        if (method.equals("aRequestSupport")) {
            this.aRequestSupport();
        } else if (method.equals("bRequestSupport")) {
            this.bRequestSupport();
        }
    }

    // A請求支援
    private void aRequestSupport() {
        System.out.println("SituationA:這裏是A戰區,如今被敵方攻打,請求支援");
        boolean isBSupport = isSupport();  // B是否能夠支援
        super.situationB.bSupport(isBSupport);
        if (!isBSupport) { // B支援不了,請求C
            System.out.println("-------A又向C請求支援--------");
            boolean isASupport = isSupport();  // B是否能夠支援
            super.situationC.cSupport(isASupport);
            if (!isASupport) {
                System.out.println("-------本身看着辦吧。--------");
            }
        }
    }

    // B請求支援
    public void bRequestSupport() {
        System.out.println("這裏是B的請求支援");
    }

    private boolean isSupport() {
        Random rand = new Random();
        return rand.nextBoolean();
    }
}

代碼比較長,但也比較簡單。定義了處理各個對象關係的業務方法,把依賴關係轉移到了這個業務方法中,而同事類只須要委託中介者協調各個同事類的業務邏輯。

public abstract class Colleague {
    protected Mediator mediator;

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

很簡單的就一個構造方法,繼續看具體同事類的實現,我先把各個同事類的代碼都貼出來:

// A戰區
public class SituationA extends Colleague {

    public SituationA(Mediator mediator) {
        super(mediator);
    }

    // 請求支援
    public void aRequestSupport() {
        super.mediator.execute("aRequestSupport");
    }
}

// B戰區
public class SituationB extends Colleague {

    public SituationB(Mediator mediator) {
        super(mediator);
    }

    // 請求支援
    public void bRequestSupport() {
        super.mediator.execute("bRequestSupport");
    }

    public void bSupport(boolean isSupport) {
        if (isSupport) {
            System.out.println("SituationB:Copy that,還有五秒鐘到達戰場");
        } else {
            System.out.println("-------B也正在被攻打--------");
            System.out.println("SituationB:支援你妹,我也正在被攻打");
        }
    }
}

// C戰區
public class SituationC extends Colleague {
    public SituationC(Mediator mediator) {
        super(mediator);
    }

    // 請求支援
    public void cRequestSupport() {
        super.mediator.execute("cRequestSupport");
    }

    public void cSupport(boolean isSupport) {
        if (isSupport) {
            System.out.println(getClass().getSimpleName() + ":Copy that,還有五秒鐘到達戰場");
        } else {
            System.out.println(getClass().getSimpleName() + ":很差意思,來遲一步了,正在前往別的戰區支援");
        }
    }
}

跟前面說的同樣,經過cRequestSupport方法中的execute委託中介者處理同事類的業務邏輯,自己只負責處理自身的業務。

最後來看客戶端的實現,代碼以下:

public class Client {
    public static void main(String[] args) {
        Mediator mediator = new Command();
        System.out.println("-------A被攻打,請求支援--------");
        SituationA situationA = new SituationA(mediator);
        situationA.aRequestSupport();
    }
}

能夠看到,表面上請求仍是從A發出,可是A已經委託了中介者進行業務邏輯和流程的處理。這樣的好處就是每一個同事類的職責都很清晰,跟其餘同事類有關聯的都委託到中介者,自己專一本身的行爲。

運行客戶端,結果以下:

-------A被攻打,請求支援--------
SituationA:這裏是A戰區,如今被敵方攻打,請求支援
-------B也正在被攻打--------
SituationB:支援你妹,我也正在被攻打
-------A又向C請求支援--------
SituationC:Copy that,還有五秒鐘到達戰場

3、中介者模式的優缺點

優勢

1)解耦。把同事類原來一對多的依賴變成一對一的依賴,下降同事類的耦合度,同時也符合了迪米特原則。

缺點

1)中介者模式把業務流程和協調都寫在中介者,當同事類越多,中介者的業務就越複雜,形成很差管理的弊端。

2)中介者模式還有一個明顯的缺點,若是要增減同事類,必須得修改抽象中介者角色和具體中介者角色類。

4、模式擴展

中介者模式和觀察者模式混編

爲何要跟觀察者模式組合混編?首先,上面提到了若是要增長或者刪除同事類,必須對中介者這個角色進行修改,由於中介者角色的業務邏輯相對比較集中和複雜,修改中介者角色會比較麻煩。另一點是,使用觀察者模式實現同事類(被觀察者)的通訊能夠優化中介者的業務邏輯流程,避免過多使用if...else。

同事類通知->中介者協調處理->中介者通知同事類

其實能夠說成中介者模式是經過觀察者模式實現的,都是事件驅動模型。這裏簡單闡述下原理,把中介者做爲觀察者,即中介者角色實現Observer接口,重寫update方法(重點就在update,同事類跟中介者,中介者月同事類之間的通訊就在這實現)。同事類繼承Observable被觀察者類,經過notifyObservers能夠與中介者通訊。這樣就在至關於觀察者模式的基礎上(觀察者模式的交互路徑較短),在中介者中增長了消息轉發的功能,也就是說同事類之間的通訊通過了中介者。

中介者模式VS門面模式

先簡單介紹下門面模式。要求一個子系統的外部與其內部的通訊必須經過一個統一的對象進行,這就是門面模式。門面模式主要的是提供了一個高層次的接口,也就是所謂的一個統一對象,經過它跟子系統進行通訊。這樣作的好處就是,第一,外部與系統內部解耦,減小相互之間的依賴;第二,增長系統內部的靈活性,系統內部變化不影響外部跟門面角色的關係。好比拍照,能夠用手機拍,也能夠用單反相機拍,把手機和單反封裝在一塊兒,外部只能看到一個攝像頭,你只能說拍照,裏面究竟是手機拍仍是單反拍是不知道的。

中介者模式和門面模式一樣的都是經過封裝一個角色來進行隔離解耦,但中介者強調的是中介協調同事類之間的通訊,門面模式則是經過門面對內部進行隔離。另外,中介者模式的通訊是雙向的,而門面模式的通訊是單向的。

總結

系統中多個對象之間相互依賴,而且依賴關係結構複雜致使對象之間的耦合度增大,修改難度大,這個時候能夠考慮使用中介者模式來梳理對象之間的通訊,達到下降對象之間耦合度的效果。中介者模式就到這,下一篇命令模式,您的點贊和關注是個人動力,欽敬欽敬!

設計模式Java源碼GitHub下載https://github.com/jetLee92/DesignPattern

更多幹貨關注微信公衆號「Jet啟思」

AndroidJet的開發之路

相關文章
相關標籤/搜索