【學(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)众眨。
在有些軟件中,某些類/對(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所示:
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所對(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)。
中介者模式可以使對(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)大量的修改工作待锈。
如果在一個(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所示:
在中介者模式結(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只是一個(gè)重構(gòu)之后的結(jié)構(gòu)示意圖盈蛮,在具體實(shí)現(xiàn)時(shí),為了確保系統(tǒng)具有更好的靈活性和可擴(kuò)展性技矮,我們需要定義抽象中介者和抽象組件類抖誉,其中抽象組件類是所有具體組件類的公共父類,完整類圖如圖20-8所示:
在圖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中我們不難發(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所示:
由于【解決方案二】無(wú)須修改ConcreteMediator類皆怕,更符合“開(kāi)閉原則”,因此我們選擇該解決方案來(lái)對(duì)新增Label類進(jìn)行處理世蔗,對(duì)應(yīng)的完整類圖如圖20-11所示:
在圖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)用絮吵。
- 主要優(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ò)展。
- 主要缺點(diǎn)
中介者模式的主要缺點(diǎn)如下:
在具體中介者類中包含了大量同事之間的交互細(xì)節(jié)嗅定,可能會(huì)導(dǎo)致具體中介者類非常復(fù)雜,使得系統(tǒng)難以維護(hù)用踩。
- 適用場(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)的控制部件鹃栽。
售票機(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 頂層用例圖
【問(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上做掉