本文節(jié)選自《設(shè)計(jì)模式就該這樣學(xué)》
1 中介者模式的應(yīng)用場(chǎng)景
在現(xiàn)實(shí)生活中擎浴,中介者的存在是不可缺少的员咽,如果沒有了中介者,我們就不能與遠(yuǎn)方的朋友進(jìn)行交流贮预。各個(gè)同事對(duì)象將會(huì)相互進(jìn)行引用贝室,如果每個(gè)對(duì)象都與多個(gè)對(duì)象進(jìn)行交互契讲,則會(huì)形成如下圖所示的網(wǎng)狀結(jié)構(gòu)。
從上圖可以看到滑频,每個(gè)對(duì)象之間都過度耦合捡偏,這樣既不利于信息的復(fù)用也不利于擴(kuò)展。如果引入中介者模式峡迷,則對(duì)象之間的關(guān)系將變成星形結(jié)構(gòu)银伟,如下圖所示。
從上圖可以看到绘搞,使用中介者模式后彤避,任何一個(gè)類的變化,只會(huì)影響中介者和類本身看杭,不像之前的設(shè)計(jì)忠藤,任何一個(gè)類的變化都會(huì)引起其關(guān)聯(lián)的所有類的變化挟伙。這樣的設(shè)計(jì)大大減少了系統(tǒng)的耦合度楼雹。
其實(shí)日常生活中我們每天都在刷的朋友圈,就是一個(gè)中介者尖阔。還有我們所見的信息交易平臺(tái)贮缅,也是中介者模式的體現(xiàn)。
中介者模式是用來降低多個(gè)對(duì)象和類之間的通信復(fù)雜性的介却。這種模式通過提供一個(gè)中介類谴供,將系統(tǒng)各層次對(duì)象間的多對(duì)多關(guān)系變成一對(duì)多關(guān)系,中介者對(duì)象可以將復(fù)雜的網(wǎng)狀結(jié)構(gòu)變成以中介者為中心的星形結(jié)構(gòu)齿坷,達(dá)到降低系統(tǒng)的復(fù)雜性桂肌、提高可擴(kuò)展性的作用。
若系統(tǒng)各層次對(duì)象之間存在大量的關(guān)聯(lián)關(guān)系永淌,即層次對(duì)象呈復(fù)雜的網(wǎng)狀結(jié)構(gòu)崎场,如果直接讓它們緊耦合通信,會(huì)使系統(tǒng)結(jié)構(gòu)變得異常復(fù)雜遂蛀,且當(dāng)其中某個(gè)層次對(duì)象發(fā)生改變時(shí)谭跨,則與其緊耦合的相應(yīng)層次對(duì)象也需進(jìn)行修改,系統(tǒng)很難進(jìn)行維護(hù)李滴。
簡(jiǎn)單地說螃宙,如果多個(gè)類相互耦合,形成了網(wǎng)狀結(jié)構(gòu)所坯,則考慮使用中介者模式進(jìn)行優(yōu)化谆扎。總結(jié)一下芹助,中介者模式主要適用于以下應(yīng)用場(chǎng)景堂湖。
(1)系統(tǒng)中對(duì)象之間存在復(fù)雜的引用關(guān)系籍凝,產(chǎn)生的相互依賴關(guān)系結(jié)構(gòu)混亂且難以理解。
(2)交互的公共行為苗缩,如果需要改變行為饵蒂,則可以增加新的中介者類。
2 中介者模式的UML類圖
中介者模式的UML類圖如下圖所示酱讶。
3 使用中介者模式設(shè)計(jì)群聊場(chǎng)景
假設(shè)我們要構(gòu)建一個(gè)聊天室系統(tǒng)退盯,用戶可以向聊天室發(fā)送消息,聊天室會(huì)向所有用戶顯示消息泻肯。實(shí)際上就是用戶發(fā)信息與聊天室顯示的通信過程渊迁,不過用戶無法直接將信息發(fā)給聊天室,而需要將信息先發(fā)到服務(wù)器上灶挟,然后服務(wù)器再將該消息發(fā)給聊天室進(jìn)行顯示琉朽,具體代碼如下。首先創(chuàng)建User類稚铣。
public class User {
private String name;
private ChatRoom chatRoom;
public User(String name, ChatRoom chatRoom) {
this.name = name;
this.chatRoom = chatRoom;
}
public void sendMessage(String msg) {
this.chatRoom.showMsg(this, msg);
}
public String getName() {
return name;
}
}
然后創(chuàng)建ChatRoom類箱叁。
public class ChatRoom {
public void showMsg(User user, String msg) {
System.out.println("[" + user.getName() + "] :" + msg);
}
}
最后編寫客戶端測(cè)試代碼。
public static void main(String[] args) {
ChatRoom room = new ChatRoom();
User tom = new User("Tom",room);
User jerry = new User("Jerry",room);
tom.sendMessage("Hi! I am Tom.");
jerry.sendMessage("Hello! My name is Jerry.");
}
運(yùn)行結(jié)果如下圖所示惕医。
4 中介者模式在JDK源碼中的應(yīng)用
首先來看JDK中的Timer類耕漱。打開Timer的結(jié)構(gòu),我們發(fā)現(xiàn)Timer類中有很多schedule()重載方法抬伺,如下圖所示螟够。
任意點(diǎn)開其中一個(gè)方法,我們發(fā)現(xiàn)所有方法最終都調(diào)用了私有的schedule()方法峡钓,源碼如下妓笙。
public class Timer {
...
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
...
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
...
}
而且,不管是什么樣的任務(wù)都被加入一個(gè)隊(duì)列中按順序執(zhí)行能岩。我們把這個(gè)隊(duì)列中的所有對(duì)象都稱為“同事”寞宫。同事之間的通信都是通過Timer來協(xié)調(diào)完成的,Timer承擔(dān)了中介者的角色捧灰。
關(guān)注『 Tom彈架構(gòu) 』回復(fù)“設(shè)計(jì)模式”可獲取完整源碼淆九。
【推薦】Tom彈架構(gòu):30個(gè)設(shè)計(jì)模式真實(shí)案例(附源碼),挑戰(zhàn)年薪60W不是夢(mèng)
本文為“Tom彈架構(gòu)”原創(chuàng)毛俏,轉(zhuǎn)載請(qǐng)注明出處炭庙。技術(shù)在于分享,我分享我快樂煌寇!
如果本文對(duì)您有幫助焕蹄,歡迎關(guān)注和點(diǎn)贊;如果您有任何建議也可留言評(píng)論或私信阀溶,您的支持是我堅(jiān)持創(chuàng)作的動(dòng)力腻脏。關(guān)注『 Tom彈架構(gòu) 』可獲取更多技術(shù)干貨鸦泳!