行為型SEQ5 - 中介者模式 Mediator Pattern

【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★☆☆☆】
直接出處:中介者模式
梳理和學(xué)習(xí):https://github.com/BruceOuyang/boy-design-pattern
簡(jiǎn)書日期: 2018/03/24
簡(jiǎn)書首頁(yè):http://www.reibang.com/p/0fb891a7c5ed

協(xié)調(diào)多個(gè)對(duì)象之間的交互——中介者模式(一)

騰訊公司推出的QQ作為一款免費(fèi)的即時(shí)聊天軟件深受廣大用戶的喜愛(ài)坚嗜,它已經(jīng)成為很多人學(xué)習(xí)夯膀、工作和生活的一部分(不要告訴我你沒(méi)有QQ哦)。在QQ聊天中苍蔬,一般有兩種聊天方式:第一種是用戶與用戶直接聊天诱建,第二種是通過(guò)QQ群聊天,如圖20-1所示碟绑。如果我們使用圖20-1(A)所示方式俺猿,一個(gè)用戶如果要與別的用戶聊天或發(fā)送文件,通常需要加其他用戶為好友格仲,用戶與用戶之間存在多對(duì)多的聯(lián)系押袍,這將導(dǎo)致系統(tǒng)中用戶之間的關(guān)系非常復(fù)雜,一個(gè)用戶如果要將相同的信息或文件發(fā)送給其他所有用戶凯肋,必須一個(gè)一個(gè)的發(fā)送谊惭,于是QQ群產(chǎn)生了,如圖20-1(B)所示侮东,如果使用QQ群圈盔,一個(gè)用戶就可以向多個(gè)用戶發(fā)送相同的信息和文件而無(wú)須一一進(jìn)行發(fā)送,只需要將信息或文件發(fā)送到群中或作為群共享即可悄雅,群的作用就是將發(fā)送者所發(fā)送的信息和文件轉(zhuǎn)發(fā)給每一個(gè)接收者用戶驱敲。通過(guò)引入群的機(jī)制,將極大減少系統(tǒng)中用戶之間的兩兩通信宽闲,用戶與用戶之間的聯(lián)系可以通過(guò)群來(lái)實(shí)現(xiàn)众眨。

圖20-1 QQ聊天示意圖

在有些軟件中,某些類/對(duì)象之間的相互調(diào)用關(guān)系錯(cuò)綜復(fù)雜容诬,類似QQ用戶之間的關(guān)系娩梨,此時(shí),我們特別需要一個(gè)類似“QQ群”一樣的中間類來(lái)協(xié)調(diào)這些類/對(duì)象之間的復(fù)雜關(guān)系览徒,以降低系統(tǒng)的耦合度姚建。有一個(gè)設(shè)計(jì)模式正為此而誕生,它就是本章將要介紹的中介者模式吱殉。

20.1 客戶信息管理窗口的初始設(shè)計(jì)

Sunny軟件公司欲開(kāi)發(fā)一套CRM系統(tǒng),其中包含一個(gè)客戶信息管理模塊厘托,所設(shè)計(jì)的“客戶信息管理窗口”界面效果圖如圖20-2所示:

圖20-2 “客戶信息管理窗口”界面圖

Sunny公司開(kāi)發(fā)人員通過(guò)分析發(fā)現(xiàn)友雳,在圖20-2中,界面組件之間存在較為復(fù)雜的交互關(guān)系:如果刪除一個(gè)客戶铅匹,要在客戶列表(List)中刪掉對(duì)應(yīng)的項(xiàng)押赊,客戶選擇組合框(ComboBox)中客戶名稱也將減少一個(gè);如果增加一個(gè)客戶信息,客戶列表中需增加一個(gè)客戶流礁,且組合框中也將增加一項(xiàng)涕俗。

如果實(shí)現(xiàn)界面組件之間的交互是Sunny公司開(kāi)發(fā)人員必須面對(duì)的一個(gè)問(wèn)題?

Sunny公司開(kāi)發(fā)人員對(duì)組件之間的交互關(guān)系進(jìn)行了分析神帅,結(jié)果如下:

(1) 當(dāng)用戶單擊“增加”按鈕再姑、“刪除”按鈕、“修改”按鈕或“查詢”按鈕時(shí)找御,界面左側(cè)的“客戶選擇組合框”元镀、“客戶列表”以及界面中的文本框?qū)a(chǎn)生響應(yīng)。

(2) 當(dāng)用戶通過(guò)“客戶選擇組合框”選中某個(gè)客戶姓名時(shí)霎桅,“客戶列表”和文本框?qū)a(chǎn)生響應(yīng)栖疑。

(3) 當(dāng)用戶通過(guò)“客戶列表”選中某個(gè)客戶姓名時(shí),“客戶選擇組合框”和文本框?qū)a(chǎn)生響應(yīng)滔驶。

于是遇革,Sunny公司開(kāi)發(fā)人員根據(jù)組件之間的交互關(guān)系繪制了如圖20-3所示初始類圖:

圖20-3 “客戶信息管理窗口”原始類圖

與類圖20-3所對(duì)應(yīng)的框架代碼片段如下:

//按鈕類  
class Button {  
    private List list;  
    private ComboBox cb;  
    private TextBox tb;  
    ......  

    //界面組件的交互  
    public void change() {  
        list.update();  
        cb.update();  
        tb.update();  
    }  

    public void update() {  
        ......  
    }  
    ......  
}  

//列表框類  
class List {  
    private ComboBox cb;  
    private TextBox tb;  
    ......  

//界面組件的交互  
    public void change() {  
        cb.update();  
        tb.update();  
    }  

    public void update() {  
        ......  
    }  
    ......    
}  

//組合框類  
class ComboBox {  
    private List list;  
    private TextBox tb;  
    ......  

//界面組件的交互  
    public void change() {  
        list.update();  
        tb.update();  
    }  

    public void update() {  
        ......  
    }  
    ......    
}  

//文本框類  
class TextBox {  
    public void update() {  
        ......  
    }  
    ......    
}

分析圖20-3所示初始結(jié)構(gòu)圖和上述代碼,我們不難發(fā)現(xiàn)該設(shè)計(jì)方案存在如下問(wèn)題:

(1) 系統(tǒng)結(jié)構(gòu)復(fù)雜且耦合度高:每一個(gè)界面組件都與多個(gè)其他組件之間產(chǎn)生相互關(guān)聯(lián)和調(diào)用揭糕,若一個(gè)界面組件對(duì)象發(fā)生變化萝快,需要跟蹤與之有關(guān)聯(lián)的其他所有組件并進(jìn)行處理,系統(tǒng)組件之間呈現(xiàn)一種較為復(fù)雜的網(wǎng)狀結(jié)構(gòu)插佛,組件之間的耦合度高杠巡。

(2) 組件的可重用性差:由于每一個(gè)組件和其他組件之間都具有很強(qiáng)的關(guān)聯(lián),若沒(méi)有其他組件的支持雇寇,一個(gè)組件很難被另一個(gè)系統(tǒng)或模塊重用氢拥,這些組件表現(xiàn)出來(lái)更像一個(gè)不可分割的整體,而在實(shí)際使用時(shí)锨侯,我們往往需要每一個(gè)組件都能夠單獨(dú)重用嫩海,而不是重用一個(gè)由多個(gè)組件組成的復(fù)雜結(jié)構(gòu)。

(3) 系統(tǒng)的可擴(kuò)展性差:如果在上述系統(tǒng)中增加一個(gè)新的組件類囚痴,則必須修改與之交互的其他組件類的源代碼叁怪,將導(dǎo)致多個(gè)類的源代碼需要修改巨税,同樣从绘,如果要?jiǎng)h除一個(gè)組件也存在類似的問(wèn)題,這違反了“開(kāi)閉原則”贵涵,可擴(kuò)展性和靈活性欠佳痴荐。

由于存在上述問(wèn)題血柳,Sunny公司開(kāi)發(fā)人員不得不對(duì)原有系統(tǒng)進(jìn)行重構(gòu),那如何重構(gòu)呢生兆?大家想到了“迪米特法則”难捌,引入一個(gè)“第三者”來(lái)降低現(xiàn)有系統(tǒng)中類之間的耦合度。由這個(gè)“第三者”來(lái)封裝并協(xié)調(diào)原有組件兩兩之間復(fù)雜的引用關(guān)系,使之成為一個(gè)松耦合的系統(tǒng)根吁,這個(gè)“第三者”又稱為“中介者”员淫,中介者模式因此而得名。下面讓我們正式進(jìn)入中介者模式的學(xué)習(xí)击敌,學(xué)會(huì)如何使用中介者類來(lái)協(xié)調(diào)多個(gè)類/對(duì)象之間的交互介返,以達(dá)到降低系統(tǒng)耦合度的目的。

協(xié)調(diào)多個(gè)對(duì)象之間的交互——中介者模式(二)

20.2 中介者模式概述

如果在一個(gè)系統(tǒng)中對(duì)象之間的聯(lián)系呈現(xiàn)為網(wǎng)狀結(jié)構(gòu)愚争,如圖20-4所示映皆。對(duì)象之間存在大量的多對(duì)多聯(lián)系,將導(dǎo)致系統(tǒng)非常復(fù)雜轰枝,這些對(duì)象既會(huì)影響別的對(duì)象捅彻,也會(huì)被別的對(duì)象所影響,這些對(duì)象稱為同事對(duì)象鞍陨,它們之間通過(guò)彼此的相互作用實(shí)現(xiàn)系統(tǒng)的行為步淹。在網(wǎng)狀結(jié)構(gòu)中,幾乎每個(gè)對(duì)象都需要與其他對(duì)象發(fā)生相互作用诚撵,而這種相互作用表現(xiàn)為一個(gè)對(duì)象與另外一個(gè)對(duì)象的直接耦合缭裆,這將導(dǎo)致一個(gè)過(guò)度耦合的系統(tǒng)。

圖20-4 對(duì)象之間存在復(fù)雜關(guān)系的網(wǎng)狀結(jié)構(gòu)

中介者模式可以使對(duì)象之間的關(guān)系數(shù)量急劇減少寿烟,通過(guò)引入中介者對(duì)象澈驼,可以將系統(tǒng)的網(wǎng)狀結(jié)構(gòu)變成以中介者為中心的星形結(jié)構(gòu),如圖20-5所示筛武。在這個(gè)星形結(jié)構(gòu)中缝其,同事對(duì)象不再直接與另一個(gè)對(duì)象聯(lián)系,它通過(guò)中介者對(duì)象與另一個(gè)對(duì)象發(fā)生相互作用徘六。中介者對(duì)象的存在保證了對(duì)象結(jié)構(gòu)上的穩(wěn)定内边,也就是說(shuō),系統(tǒng)的結(jié)構(gòu)不會(huì)因?yàn)樾聦?duì)象的引入帶來(lái)大量的修改工作待锈。

圖20-5 引入中介者對(duì)象的星型結(jié)構(gòu)

如果在一個(gè)系統(tǒng)中對(duì)象之間存在多對(duì)多的相互關(guān)系漠其,我們可以將對(duì)象之間的一些交互行為從各個(gè)對(duì)象中分離出來(lái),并集中封裝在一個(gè)中介者對(duì)象中竿音,并由該中介者進(jìn)行統(tǒng)一協(xié)調(diào)和屎,這樣對(duì)象之間多對(duì)多的復(fù)雜關(guān)系就轉(zhuǎn)化為相對(duì)簡(jiǎn)單的一對(duì)多關(guān)系。通過(guò)引入中介者來(lái)簡(jiǎn)化對(duì)象之間的復(fù)雜交互春瞬,中介者模式是“迪米特法則”的一個(gè)典型應(yīng)用柴信。

中介者模式定義如下:

中介者模式(Mediator Pattern):用一個(gè)中介對(duì)象(中介者)來(lái)封裝一系列的對(duì)象交互,中介者使各對(duì)象不需要顯式地相互引用快鱼,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互。中介者模式又稱為調(diào)停者模式抹竹,它是一種對(duì)象行為型模式线罕。

在中介者模式中,我們引入了用于協(xié)調(diào)其他對(duì)象/類之間相互調(diào)用的中介者類窃判,為了讓系統(tǒng)具有更好的靈活性和可擴(kuò)展性钞楼,通常還提供了抽象中介者,其結(jié)構(gòu)圖如圖20-6所示:

圖20-6 中介者模式結(jié)構(gòu)圖

在中介者模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Mediator(抽象中介者):它定義一個(gè)接口袄琳,該接口用于與各同事對(duì)象之間進(jìn)行通信询件。

  • ConcreteMediator(具體中介者):它是抽象中介者的子類,通過(guò)協(xié)調(diào)各個(gè)同事對(duì)象來(lái)實(shí)現(xiàn)協(xié)作行為唆樊,它維持了對(duì)各個(gè)同事對(duì)象的引用宛琅。

  • Colleague(抽象同事類):它定義各個(gè)同事類公有的方法,并聲明了一些抽象方法來(lái)供子類實(shí)現(xiàn)逗旁,同時(shí)它維持了一個(gè)對(duì)抽象中介者類的引用嘿辟,其子類可以通過(guò)該引用來(lái)與中介者通信。

  • ConcreteColleague(具體同事類):它是抽象同事類的子類片效;每一個(gè)同事對(duì)象在需要和其他同事對(duì)象通信時(shí)红伦,先與中介者通信,通過(guò)中介者來(lái)間接完成與其他同事類的通信淀衣;在具體同事類中實(shí)現(xiàn)了在抽象同事類中聲明的抽象方法昙读。

中介者模式的核心在于中介者類的引入,在中介者模式中膨桥,中介者類承擔(dān)了兩方面的職責(zé):

(1) 中轉(zhuǎn)作用(結(jié)構(gòu)性):通過(guò)中介者提供的中轉(zhuǎn)作用蛮浑,各個(gè)同事對(duì)象就不再需要顯式引用其他同事,當(dāng)需要和其他同事進(jìn)行通信時(shí)国撵,可通過(guò)中介者來(lái)實(shí)現(xiàn)間接調(diào)用陵吸。該中轉(zhuǎn)作用屬于中介者在結(jié)構(gòu)上的支持。

(2) 協(xié)調(diào)作用(行為性):中介者可以更進(jìn)一步的對(duì)同事之間的關(guān)系進(jìn)行封裝介牙,同事可以一致的和中介者進(jìn)行交互壮虫,而不需要指明中介者需要具體怎么做,中介者根據(jù)封裝在自身內(nèi)部的協(xié)調(diào)邏輯环础,對(duì)同事的請(qǐng)求進(jìn)行進(jìn)一步處理囚似,將同事成員之間的關(guān)系行為進(jìn)行分離和封裝。該協(xié)調(diào)作用屬于中介者在行為上的支持线得。

在中介者模式中饶唤,典型的抽象中介者類代碼如下所示:

abstract class Mediator {  
    protected ArrayList<Colleague> colleagues; //用于存儲(chǔ)同事對(duì)象  

    //注冊(cè)方法,用于增加同事對(duì)象  
    public void register(Colleague colleague) {  
        colleagues.add(colleague);  
    }  

    //聲明抽象的業(yè)務(wù)方法  
    public abstract void operation();  
} 

在抽象中介者中可以定義一個(gè)同事類的集合贯钩,用于存儲(chǔ)同事對(duì)象并提供注冊(cè)方法募狂,同時(shí)聲明了具體中介者類所具有的方法办素。在具體中介者類中將實(shí)現(xiàn)這些抽象方法,典型的具體中介者類代碼如下所示:

class ConcreteMediator extends Mediator {  
    //實(shí)現(xiàn)業(yè)務(wù)方法祸穷,封裝同事之間的調(diào)用  
    public void operation() {  
        ......  
        ((Colleague)(colleagues.get(0))).method1(); //通過(guò)中介者調(diào)用同事類的方法  
        ......  
    }  
}

在具體中介者類中將調(diào)用同事類的方法性穿,調(diào)用時(shí)可以增加一些自己的業(yè)務(wù)代碼對(duì)調(diào)用進(jìn)行控制。

在抽象同事類中維持了一個(gè)抽象中介者的引用雷滚,用于調(diào)用中介者的方法需曾,典型的抽象同事類代碼如下所示:

abstract class Colleague {  
    protected Mediator mediator; //維持一個(gè)抽象中介者的引用  

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

    public abstract void method1(); //聲明自身方法,處理自己的行為  

    //定義依賴方法祈远,與中介者進(jìn)行通信  
    public void method2() {  
        mediator.operation();  
    }  
}

在抽象同事類中聲明了同事類的抽象方法呆万,而在具體同事類中將實(shí)現(xiàn)這些方法,典型的具體同事類代碼如下所示:

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

    //實(shí)現(xiàn)自身方法  
    public void method1() {  
        ......  
    }  
}

在具體同事類ConcreteColleague中實(shí)現(xiàn)了在抽象同事類中聲明的方法车份,其中方法method1()是同事類的自身方法(Self-Method)谋减,用于處理自己的行為,而方法method2()是依賴方法(Depend-Method)躬充,用于調(diào)用在中介者中定義的方法逃顶,依賴中介者來(lái)完成相應(yīng)的行為,例如調(diào)用另一個(gè)同事類的相關(guān)方法充甚。

思考

如何理解同事類中的自身方法與依賴方法以政?

協(xié)調(diào)多個(gè)對(duì)象之間的交互——中介者模式(三)

20.3 完整解決方案

為了協(xié)調(diào)界面組件對(duì)象之間的復(fù)雜交互關(guān)系,Sunny公司開(kāi)發(fā)人員使用中介者模式來(lái)設(shè)計(jì)客戶信息管理窗口伴找,其結(jié)構(gòu)示意圖如圖20-7所示:

圖20-7 引入了中介者類的“客戶信息管理窗口”結(jié)構(gòu)示意圖

圖20-7只是一個(gè)重構(gòu)之后的結(jié)構(gòu)示意圖盈蛮,在具體實(shí)現(xiàn)時(shí),為了確保系統(tǒng)具有更好的靈活性和可擴(kuò)展性技矮,我們需要定義抽象中介者和抽象組件類抖誉,其中抽象組件類是所有具體組件類的公共父類,完整類圖如圖20-8所示:

圖20-8 重構(gòu)后的“客戶信息管理窗口”結(jié)構(gòu)圖

在圖20-8中衰倦,Component充當(dāng)抽象同事類袒炉,Button、List樊零、ComboBox和TextBox充當(dāng)具體同事類我磁,Mediator充當(dāng)抽象中介者類,ConcreteMediator充當(dāng)具體中介者類驻襟,ConcreteMediator維持了對(duì)具體同事類的引用夺艰,為了簡(jiǎn)化ConcreteMediator類的代碼,我們?cè)谄渲兄欢x了一個(gè)Button對(duì)象和一個(gè)TextBox對(duì)象沉衣。完整代碼如下所示:

//抽象中介者  
abstract class Mediator {  
    public abstract void componentChanged(Component c);  
}  

//具體中介者  
class ConcreteMediator extends Mediator {  
    //維持對(duì)各個(gè)同事對(duì)象的引用  
    public Button addButton;  
    public List list;  
    public TextBox userNameTextBox;  
    public ComboBox cb;  

    //封裝同事對(duì)象之間的交互  
    public void componentChanged(Component c) {  
        //單擊按鈕  
if(c == addButton) {  
            System.out.println("--單擊增加按鈕--");  
            list.update();  
            cb.update();  
            userNameTextBox.update();  
        }  
        //從列表框選擇客戶  
        else if(c == list) {  
            System.out.println("--從列表框選擇客戶--");  
            cb.select();  
            userNameTextBox.setText();  
        }  
        //從組合框選擇客戶  
        else if(c == cb) {  
            System.out.println("--從組合框選擇客戶--");  
            cb.select();  
            userNameTextBox.setText();  
        }  
    }  
}  

//抽象組件類:抽象同事類  
abstract class Component {  
    protected Mediator mediator;  

    public void setMediator(Mediator mediator) {  
        this.mediator = mediator;  
    }  

    //轉(zhuǎn)發(fā)調(diào)用  
    public void changed() {  
        mediator.componentChanged(this);  
    }  

    public abstract void update();    
}  

//按鈕類:具體同事類  
class Button extends Component {  
    public void update() {  
        //按鈕不產(chǎn)生交互  
    }  
}  

//列表框類:具體同事類  
class List extends Component {  
    public void update() {  
        System.out.println("列表框增加一項(xiàng):張無(wú)忌郁副。");  
    }  

    public void select() {  
        System.out.println("列表框選中項(xiàng):小龍女。");  
    }  
}  

//組合框類:具體同事類  
class ComboBox extends Component {  
    public void update() {  
        System.out.println("組合框增加一項(xiàng):張無(wú)忌豌习。");  
    }  

    public void select() {  
        System.out.println("組合框選中項(xiàng):小龍女存谎。");  
    }  
}  

//文本框類:具體同事類  
class TextBox extends Component {  
    public void update() {  
        System.out.println("客戶信息增加成功后文本框清空拔疚。");  
    }  

    public void setText() {  
        System.out.println("文本框顯示:小龍女。");  
    }  
}

編寫如下客戶端測(cè)試代碼:

class Client {  
    public static void main(String args[]) {  
        //定義中介者對(duì)象  
        ConcreteMediator mediator;  
        mediator = new ConcreteMediator();  

        //定義同事對(duì)象  
        Button addBT = new Button();  
        List list = new List();  
        ComboBox cb = new ComboBox();  
        TextBox userNameTB = new TextBox();  

        addBT.setMediator(mediator);  
        list.setMediator(mediator);  
        cb.setMediator(mediator);  
        userNameTB.setMediator(mediator);  

        mediator.addButton = addBT;  
        mediator.list = list;  
        mediator.cb = cb;  
        mediator.userNameTextBox = userNameTB;  

        addBT.changed();  
        System.out.println("-----------------------------");  
        list.changed();  
    }  
}

編譯并運(yùn)行程序既荚,輸出結(jié)果如下:

--單擊增加按鈕--
列表框增加一項(xiàng):張無(wú)忌草雕。
組合框增加一項(xiàng):張無(wú)忌。
客戶信息增加成功后文本框清空固以。
-----------------------------
--從列表框選擇客戶--
組合框選中項(xiàng):小龍女。
文本框顯示:小龍女嘱巾。

協(xié)調(diào)多個(gè)對(duì)象之間的交互——中介者模式(四)

20.4 中介者與同事類的擴(kuò)展

Sunny軟件公司CRM系統(tǒng)的客戶對(duì)“客戶信息管理窗口”提出了一個(gè)修改意見(jiàn):要求在窗口的下端能夠及時(shí)顯示當(dāng)前系統(tǒng)中客戶信息的總數(shù)憨琳。修改之后的界面如圖20-9所示:

圖20-9 修改之后的“客戶信息管理窗口”界面圖

從圖20-9中我們不難發(fā)現(xiàn),可以通過(guò)增加一個(gè)文本標(biāo)簽(Label)來(lái)顯示客戶信息總數(shù)旬昭,而且當(dāng)用戶點(diǎn)擊“增加”按鈕或者“刪除”按鈕時(shí)篙螟,將改變文本標(biāo)簽的內(nèi)容。

由于使用了中介者模式问拘,在原有系統(tǒng)中增加新的組件(即新的同事類)將變得很容易遍略,我們至少有如下兩種解決方案:

【解決方案一】增加一個(gè)界面組件類Label,修改原有的具體中介者類ConcreteMediator骤坐,增加一個(gè)對(duì)Label對(duì)象的引用绪杏,然后修改componentChanged()方法中其他相關(guān)組件對(duì)象的業(yè)務(wù)處理代碼,原有組件類無(wú)須任何修改纽绍,客戶端代碼也需針對(duì)新增組件Label進(jìn)行適當(dāng)修改蕾久。

【解決方案二】與方案一相同,首先增加一個(gè)Label類拌夏,但不修改原有具體中介者類ConcreteMediator的代碼僧著,而是增加一個(gè)ConcreteMediator的子類SubConcreteMediator來(lái)實(shí)現(xiàn)對(duì)Label對(duì)象的引用,然后在新增的中介者類SubConcreteMediator中通過(guò)覆蓋componentChanged()方法來(lái)實(shí)現(xiàn)所有組件(包括新增Label組件)之間的交互障簿,同樣盹愚,原有組件類無(wú)須做任何修改,客戶端代碼需少許修改站故。

引入Label之后“客戶信息管理窗口”類結(jié)構(gòu)示意圖如圖20-10所示:

圖20-10 增加Label組件類后的“客戶信息管理窗口”結(jié)構(gòu)示意圖

由于【解決方案二】無(wú)須修改ConcreteMediator類皆怕,更符合“開(kāi)閉原則”,因此我們選擇該解決方案來(lái)對(duì)新增Label類進(jìn)行處理世蔗,對(duì)應(yīng)的完整類圖如圖20-11所示:

圖20-11 修改之后的“客戶信息管理窗口”結(jié)構(gòu)圖

在圖20-11中端逼,新增了具體同事類Label和具體中介者類SubConcreteMediator,代碼如下所示:

//文本標(biāo)簽類:具體同事類  
class Label extends Component {  
    public void update() {  
        System.out.println("文本標(biāo)簽內(nèi)容改變污淋,客戶信息總數(shù)加1顶滩。");  
    }  
}  

//新增具體中介者類  
class SubConcreteMediator extends ConcreteMediator {  
    //增加對(duì)Label對(duì)象的引用  
    public Label label;  

    public void componentChanged(Component c) {  
        //單擊按鈕  
if(c == addButton) {  
            System.out.println("--單擊增加按鈕--");  
            list.update();  
            cb.update();  
            userNameTextBox.update();  
            label.update(); //文本標(biāo)簽更新  
        }  
        //從列表框選擇客戶  
        else if(c == list) {  
            System.out.println("--從列表框選擇客戶--");  
            cb.select();  
            userNameTextBox.setText();  
        }  
        //從組合框選擇客戶  
        else if(c == cb) {  
            System.out.println("--從組合框選擇客戶--");  
            cb.select();  
            userNameTextBox.setText();  
        }  
    }  
}

修改客戶端測(cè)試代碼:

class Client {  
    public static void main(String args[]) {  
        //用新增具體中介者定義中介者對(duì)象  
        SubConcreteMediator mediator;  
        mediator = new SubConcreteMediator();  

        Button addBT = new Button();  
        List list = new List();  
        ComboBox cb = new ComboBox();  
        TextBox userNameTB = new TextBox();  
        Label label = new Label();  

        addBT.setMediator(mediator);  
        list.setMediator(mediator);  
        cb.setMediator(mediator);  
        userNameTB.setMediator(mediator);  
        label.setMediator(mediator);  

        mediator.addButton = addBT;  
        mediator.list = list;  
        mediator.cb = cb;  
        mediator.userNameTextBox = userNameTB;  
        mediator.label = label;  

        addBT.changed();  
        System.out.println("-----------------------------");  
        list.changed();  
    }  
}

編譯并運(yùn)行程序,輸出結(jié)果如下:

--單擊增加按鈕--
列表框增加一項(xiàng):張無(wú)忌寸爆。
組合框增加一項(xiàng):張無(wú)忌礁鲁。
客戶信息增加成功后文本框清空盐欺。
文本標(biāo)簽內(nèi)容改變,客戶信息總數(shù)加1仅醇。
-----------------------------
--從列表框選擇客戶--
組合框選中項(xiàng):小龍女冗美。
文本框顯示:小龍女。

由于在本實(shí)例中不同的組件類(即不同的同事類)所擁有的方法并不完全相同析二,因此中介者類沒(méi)有針對(duì)抽象同事類編程粉洼,導(dǎo)致在具體中介者類中需要維持對(duì)具體同事類的引用,客戶端代碼無(wú)法完全透明地對(duì)待所有同事類和中介者類叶摄。在某些情況下属韧,如果設(shè)計(jì)得當(dāng),可以在客戶端透明地對(duì)同事類和中介者類編程蛤吓,這樣系統(tǒng)將具有更好的靈活性和可擴(kuò)展性宵喂。

思考

如果不使用中介者模式,按照?qǐng)D20-3所示設(shè)計(jì)方案会傲,增加新組件時(shí)原有系統(tǒng)該如何修改锅棕?

在中介者模式的實(shí)際使用過(guò)程中,如果需要引入新的具體同事類淌山,只需要繼承抽象同事類并實(shí)現(xiàn)其中的方法即可裸燎,由于具體同事類之間并無(wú)直接的引用關(guān)系,因此原有所有同事類無(wú)須進(jìn)行任何修改泼疑,它們與新增同事對(duì)象之間的交互可以通過(guò)修改或者增加具體中介者類來(lái)實(shí)現(xiàn)顺少;如果需要在原有系統(tǒng)中增加新的具體中介者類,只需要繼承抽象中介者類(或已有的具體中介者類)并覆蓋其中定義的方法即可王浴,在新的具體中介者中可以通過(guò)不同的方式來(lái)處理對(duì)象之間的交互脆炎,也可以增加對(duì)新增同事的引用和調(diào)用。在客戶端中只需要修改少許代碼(如果引入配置文件的話有時(shí)可以不修改任何代碼)就可以實(shí)現(xiàn)中介者的更換氓辣。

協(xié)調(diào)多個(gè)對(duì)象之間的交互——中介者模式(五)

20.4 中介者模式總結(jié)

中介者模式將一個(gè)網(wǎng)狀的系統(tǒng)結(jié)構(gòu)變成一個(gè)以中介者對(duì)象為中心的星形結(jié)構(gòu)秒裕,在這個(gè)星型結(jié)構(gòu)中,使用中介者對(duì)象與其他對(duì)象的一對(duì)多關(guān)系來(lái)取代原有對(duì)象之間的多對(duì)多關(guān)系钞啸。中介者模式在事件驅(qū)動(dòng)類軟件中應(yīng)用較為廣泛几蜻,特別是基于GUI(Graphical User Interface,圖形用戶界面)的應(yīng)用軟件体斩,此外梭稚,在類與類之間存在錯(cuò)綜復(fù)雜的關(guān)聯(lián)關(guān)系的系統(tǒng)中,中介者模式都能得到較好的應(yīng)用絮吵。

  1. 主要優(yōu)點(diǎn)
    中介者模式的主要優(yōu)點(diǎn)如下:

(1) 中介者模式簡(jiǎn)化了對(duì)象之間的交互弧烤,它用中介者和同事的一對(duì)多交互代替了原來(lái)同事之間的多對(duì)多交互,一對(duì)多關(guān)系更容易理解蹬敲、維護(hù)和擴(kuò)展暇昂,將原本難以理解的網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換成相對(duì)簡(jiǎn)單的星型結(jié)構(gòu)莺戒。

(2) 中介者模式可將各同事對(duì)象解耦。中介者有利于各同事之間的松耦合急波,我們可以獨(dú)立的改變和復(fù)用每一個(gè)同事和中介者从铲,增加新的中介者和新的同事類都比較方便,更好地符合“開(kāi)閉原則”澄暮。

(3) 可以減少子類生成名段,中介者將原本分布于多個(gè)對(duì)象間的行為集中在一起,改變這些行為只需生成新的中介者子類即可泣懊,這使各個(gè)同事類可被重用吉嫩,無(wú)須對(duì)同事類進(jìn)行擴(kuò)展。

  1. 主要缺點(diǎn)
    中介者模式的主要缺點(diǎn)如下:

在具體中介者類中包含了大量同事之間的交互細(xì)節(jié)嗅定,可能會(huì)導(dǎo)致具體中介者類非常復(fù)雜,使得系統(tǒng)難以維護(hù)用踩。

  1. 適用場(chǎng)景

在以下情況下可以考慮使用中介者模式:

(1) 系統(tǒng)中對(duì)象之間存在復(fù)雜的引用關(guān)系渠退,系統(tǒng)結(jié)構(gòu)混亂且難以理解。

(2) 一個(gè)對(duì)象由于引用了其他很多對(duì)象并且直接和這些對(duì)象通信脐彩,導(dǎo)致難以復(fù)用該對(duì)象碎乃。

(3) 想通過(guò)一個(gè)中間類來(lái)封裝多個(gè)類中的行為,而又不想生成太多的子類惠奸∶肥模可以通過(guò)引入中介者類來(lái)實(shí)現(xiàn),在中介者中定義對(duì)象交互的公共行為佛南,如果需要改變行為則可以增加新的具體中介者類梗掰。

練習(xí)

Sunny軟件公司欲開(kāi)發(fā)一套圖形界面類庫(kù)。該類庫(kù)需要包含若干預(yù)定義的窗格(Pane)對(duì)象嗅回,例如TextPane及穗、ListPane、GraphicPane等绵载,窗格之間不允許直接引用埂陆。基于該類庫(kù)的應(yīng)用由一個(gè)包含一組窗格的窗口(Window)組成娃豹,窗口需要協(xié)調(diào)窗格之間的行為焚虱。試采用中介者模式設(shè)計(jì)該系統(tǒng)。

2010年上半年 軟件設(shè)計(jì)師 下午試卷 第三題

【說(shuō)明】

某運(yùn)輸公司決定為新的售票機(jī)開(kāi)發(fā)車票銷售的控制軟件懂版。圖I給出了售票機(jī)的面板示意圖以及相關(guān)的控制部件鹃栽。


圖I 售票機(jī)面板示意圖

售票機(jī)相關(guān)部件的作用如下所述:

(1) 目的地鍵盤用來(lái)輸入行程目的地的代碼(例如,200表示總站)躯畴。

(2) 乘客可以通過(guò)車票鍵盤選擇車票種類(單程票谍咆、多次往返票和座席種類)禾锤。

(3) 繼續(xù)/取消鍵盤上的取消按鈕用于取消購(gòu)票過(guò)程,繼續(xù)按鈕允許乘客連續(xù)購(gòu)買多張票摹察。

(4) 顯示屏顯示所有的系統(tǒng)輸出和用戶提示信息恩掷。

(5) 插卡口接受MCard(現(xiàn)金卡),硬幣口和紙幣槽接受現(xiàn)金供嚎。

(6) 打印機(jī)用于輸出車票黄娘。

(7) 所有部件均可實(shí)現(xiàn)自檢并恢復(fù)到初始狀態(tài)。

現(xiàn)采用面向?qū)ο蠓椒ㄩ_(kāi)發(fā)該系統(tǒng)克滴,使用UML進(jìn)行建模逼争,系統(tǒng)的頂層用例圖和類圖分別如圖II和圖III所示。

圖II 頂層用例圖


圖III 類圖

【問(wèn)題1】

根據(jù)說(shuō)明中的描述劝赔,給出圖II中A1和A2所對(duì)應(yīng)的執(zhí)行者誓焦,U1所對(duì)應(yīng)的用例,以及(1)着帽、(2)處所對(duì)應(yīng)的關(guān)系杂伟。

【問(wèn)題2】

根據(jù)說(shuō)明中的描述,給出圖III中缺少的C1-C4所對(duì)應(yīng)的類名以及(3)-(6)處所對(duì)應(yīng)的多重度仍翰。

【問(wèn)題3】

圖III中的類圖設(shè)計(jì)采用了中介者(Mediator)設(shè)計(jì)模式赫粥,請(qǐng)說(shuō)明該模式的內(nèi)涵。

練習(xí)會(huì)在我的github上做掉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末予借,一起剝皮案震驚了整個(gè)濱河市越平,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灵迫,老刑警劉巖秦叛,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瀑粥,居然都是意外死亡书闸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門利凑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浆劲,“玉大人,你說(shuō)我怎么就攤上這事哀澈∨平瑁” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵割按,是天一觀的道長(zhǎng)膨报。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么现柠? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任院领,我火速辦了婚禮,結(jié)果婚禮上够吩,老公的妹妹穿的比我還像新娘比然。我一直安慰自己,他們只是感情好周循,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布强法。 她就那樣靜靜地躺著,像睡著了一般湾笛。 火紅的嫁衣襯著肌膚如雪饮怯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天嚎研,我揣著相機(jī)與錄音蓖墅,去河邊找鬼。 笑死临扮,一個(gè)胖子當(dāng)著我的面吹牛论矾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播公条,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼迂曲!你這毒婦竟也來(lái)了靶橱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤路捧,失蹤者是張志新(化名)和其女友劉穎关霸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杰扫,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡队寇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了章姓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佳遣。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凡伊,靈堂內(nèi)的尸體忽然破棺而出零渐,到底是詐尸還是另有隱情,我是刑警寧澤系忙,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布诵盼,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏风宁。R本人自食惡果不足惜洁墙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望戒财。 院中可真熱鬧热监,春花似錦、人聲如沸固翰。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)骂际。三九已至疗琉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歉铝,已是汗流浹背盈简。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留太示,地道東北人柠贤。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像类缤,于是被迫代替她去往敵國(guó)和親臼勉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 如果沒(méi)有主板## 大家都知道餐弱,電腦里面各個(gè)配件之間的交互宴霸,主要是通過(guò)主板來(lái)完成的(事實(shí)上主...
    七寸知架構(gòu)閱讀 2,170評(píng)論 0 56
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用膏蚓、多...
    MinoyJet閱讀 3,939評(píng)論 1 15
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是中介者模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 優(yōu)點(diǎn)和缺點(diǎn) 適用環(huán)境 模式應(yīng)用 一...
    w1992wishes閱讀 1,112評(píng)論 0 4
  • 早上驮瞧,去公司天臺(tái)吸煙氓扛,舉目四望,能夠突出感覺(jué)到空氣質(zhì)量不好论笔,以前南站清晰可見(jiàn)采郎,但今晨南站已籠在霧霾手中,視野并不...
    石頭1975閱讀 80評(píng)論 0 0
  • 二零一五年狂魔,十二月二十一日尉剩,我們大三! 汗蒸毅臊。嘮嗑理茎。撲克黑界。愜意。皂林。 發(fā)哥的撲克技術(shù)朗鸠,實(shí)在不敢恭維,自己定的規(guī)則础倍,想...
    小武大大啦啦啦閱讀 318評(píng)論 0 0