本文節選自《設計模式就該這樣學》
1 中介者模式的應用場景
在現實生活中,中介者的存在是不可缺少的,如果沒有了中介者,我們就不能與遠方的朋友進行交流。各個同事對象將會相互進行引用,如果每個對象都與多個對象進行交互,則會形成如下圖所示的網狀結構。
從上圖可以看到,每個對象之間都過度耦合,這樣既不利于信息的復用也不利于擴展。如果引入中介者模式,則對象之間的關系將變成星形結構,如下圖所示。
從上圖可以看到,使用中介者模式后,任何一個類的變化,只會影響中介者和類本身,不像之前的設計,任何一個類的變化都會引起其關聯的所有類的變化。這樣的設計大大減少了系統的耦合度。
其實日常生活中我們每天都在刷的朋友圈,就是一個中介者。還有我們所見的信息交易平臺,也是中介者模式的體現。
中介者模式是用來降低多個對象和類之間的通信復雜性的。這種模式通過提供一個中介類,將系統各層次對象間的多對多關系變成一對多關系,中介者對象可以將復雜的網狀結構變成以中介者為中心的星形結構,達到降低系統的復雜性、提高可擴展性的作用。
若系統各層次對象之間存在大量的關聯關系,即層次對象呈復雜的網狀結構,如果直接讓它們緊耦合通信,會使系統結構變得異常復雜,且當其中某個層次對象發生改變時,則與其緊耦合的相應層次對象也需進行修改,系統很難進行維護。
簡單地說,如果多個類相互耦合,形成了網狀結構,則考慮使用中介者模式進行優化??偨Y一下,中介者模式主要適用于以下應用場景。
(1)系統中對象之間存在復雜的引用關系,產生的相互依賴關系結構混亂且難以理解。
(2)交互的公共行為,如果需要改變行為,則可以增加新的中介者類。
2 中介者模式的UML類圖
中介者模式的UML類圖如下圖所示。
3 使用中介者模式設計群聊場景
假設我們要構建一個聊天室系統,用戶可以向聊天室發送消息,聊天室會向所有用戶顯示消息。實際上就是用戶發信息與聊天室顯示的通信過程,不過用戶無法直接將信息發給聊天室,而需要將信息先發到服務器上,然后服務器再將該消息發給聊天室進行顯示,具體代碼如下。首先創建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; }}
然后創建ChatRoom類。
public class ChatRoom { public void showMsg(User user, String msg) { System.out.println("[" + user.getName() + "] :" + msg); }}
最后編寫客戶端測試代碼。
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.");}
運行結果如下圖所示。
4 中介者模式在JDK源碼中的應用
首先來看JDK中的Timer類。打開Timer的結構,我們發現Timer類中有很多schedule()重載方法,如下圖所示。
任意點開其中一個方法,我們發現所有方法最終都調用了私有的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(); } } ...}
而且,不管是什么樣的任務都被加入一個隊列中按順序執行。我們把這個隊列中的所有對象都稱為“同事”。同事之間的通信都是通過Timer來協調完成的,Timer承擔了中介者的角色。
關注微信公眾號『 Tom彈架構 』回復“設計模式”可獲取完整源碼。
【推薦】Tom彈架構:30個設計模式真實案例(附源碼),挑戰年薪60W不是夢
本文為“Tom彈架構”原創,轉載請注明出處。技術在于分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!