一、定義
中介者模式(Mediator Pattern) 又稱為調解者模式或調停者模式。用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。屬於行為型模式。中介者模式包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯作用。從而使它們可以松散耦合。當某些對象之間的作用發生改變時,不會立即影響其他的一些對象之間的作用。保證這些作用可以彼此獨立的變化。其核心思想是,通過中介者解耦系統各層次對象的直接耦合,層次對象的對外依賴通信統統交由中介者轉發。
中介者模式主要包含四個角色:
- 抽象中介者(Mediator) :定義統一的接口, 用於各同事角色之間的通信;
- 具體中介者(Concrete Mediator) :從具體的同事對象接收消息, 向具體同事對象發出命令,協調各同事間的協作;
- 抽象同事類(Colleague) :每一個同事對象均需要依賴中介者角色, 與其他同事間通信時,交由中介者進行轉發協作;
- 具體同事類(Concrete Colleague) :負責實現自發行為(Self-Method) , 轉發依賴方法(Dep-Method) 交由中介者進行協調。
二、中介者模式的案例
在現實生活中,中介者的存在是不可缺少的,如果沒有了中介者,我們就不能與遠方的朋友進行交流了。各個同事對象將會相互進行引用,如果每個對象都與多個對象進行交互時,將會形成如下圖所示的網狀結構。
從上圖可以發現,每個對象之間過度耦合,這樣的既不利於信息的復用也不利於擴展。如果引入了中介者模式,那么對象之間的關系將變成星型結構,采用中介者模式之后會形成如下圖所示的結構:
從上圖可以發現,使用中介者模式之后,任何一個類的變化,只會影響中介者和類本身,不像之前的設計,任何一個類的變化都會引起其關聯所有類的變化。這樣的設計大大減少了系統的耦合度。
中介者模式是用來降低多個對象和類之間的通信復雜性。這種模式通過提供一個中介類,將系統各層次對象間的多對多關系變成一對多關系,中介者對象可以將復雜的網狀結構變成以調停者為中心的星形結構,達到降低系統的復雜性,提高可擴展性的作用。若系統各層次對象之間存在大量的關聯關系,即層次對象呈復雜的網狀結構,如果直接讓它們緊耦合通信,會造成系統結構變得異常復雜,且其中某個層次對象發生改變,則與其緊耦合的相應層次對象也需進行修改,系統很難進行維護。而通過為該系統增加一個中介者層次對象,讓其他各層次需對外通信的行為統統交由中介者進行轉發,系統呈現以中介者為中心進行通訊的星形結構,系統的復雜性大大降低。簡單的說就是多個類相互耦合,形成了網狀結構,則考慮使用中介者模式進行優化。中介者模式適用於以下場景:
- 系統中對象之間存在復雜的引用關系,產生的相互依賴關系結構混亂且難以理解;
- 交互的公共行為,如果需要改變行為則可以增加新的中介者類。
1、群聊場景
假設我們要構建一個聊天室系統,用戶可以向聊天室發送消息,聊天室會向所有的用戶顯示消息,實際上就是用戶發信息與聊天室顯示的通信過程,不過用戶無法直接將信息發給聊天室,而是需要將信息先發到服務器上,然后服務器再將該消息發給聊天室進行顯示。具體代碼如下。創建User類:
public class User { private String name; private ChatRoom chatRoom; public User(String name, ChatRoom chatRoom) { this.name = name; this.chatRoom = chatRoom; } public String getName() { return name; } public void sendMessage(String msg){ this.chatRoom.showMsg(this,msg); } }
然后是ChatRoom:
public class ChatRoom { public void showMsg(User user,String msg){ System.out.println("[" + user.getName() + "] : " + msg); } }
測試:
public class Test { public static void main(String[] args) { ChatRoom chatRoom = new ChatRoom(); User zs = new User("張三",chatRoom); User ls = new User("李四",chatRoom); zs.sendMessage("您好"); ls.sendMessage("有啥事"); } }
上面這么寫估計也很簡單,下面按中介者定義來寫一個
// 抽象中介者 public abstract class Mediator { protected ConcreteColleagueA colleagueA; protected ConcreteColleagueB colleagueB; public void setColleageA(ConcreteColleagueA colleague) { this.colleagueA = colleague; } public void setColleageB(ConcreteColleagueB colleague) { this.colleagueB = colleague; } // 中介者業務邏輯 public abstract void transferA(); public abstract void transferB(); }
// 具體中介者 public class ConcreteMediator extends Mediator { @Override public void transferA() { // 協調行為:A 轉發到 B this.colleagueB.selfMethodB(); } @Override public void transferB() { // 協調行為:B 轉發到 A this.colleagueA.selfMethodA(); } }
// 抽象同事類 public abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) { this.mediator = mediator; } }
// 具體同事類 public class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); this.mediator.setColleageA(this); } // 自有方法 public void selfMethodA() { // 處理自己的邏輯 System.out.println(String.format("自有方法", this.getClass().getSimpleName())); } // 依賴方法 public void depMethodA() { // 處理自己的邏輯 System.out.println(String.format("依賴方法", this.getClass().getSimpleName())); // 無法處理的業務邏輯委托給中介者處理 this.mediator.transferA(); } }
// 具體同事類 public class ConcreteColleagueB extends Colleague { public ConcreteColleagueB(Mediator mediator) { super(mediator); this.mediator.setColleageB(this); } // 自有方法 public void selfMethodB() { // 處理自己的邏輯 System.out.println(String.format("自有方法", this.getClass().getSimpleName())); } // 依賴方法 public void depMethodB() { // 處理自己的邏輯 System.out.println(String.format("依賴方法", this.getClass().getSimpleName())); // 無法處理的業務邏輯委托給中介者處理 this.mediator.transferB(); } }
public class Test { public static void main(String[] args) { Mediator mediator = new ConcreteMediator(); ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator); ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator); colleagueA.depMethodA(); System.out.println("-------------------------"); colleagueB.depMethodB(); } }
三、中介者模式在源碼中的體現
在JDK的Timer類中有很多的schedule()重載方法,如下圖:
隨便點開一個方法,會發現所有的方法最終都是調用了私有的sched()方法,下面看下Timer源碼
public class Timer { ...... public void schedule(TimerTask var1, long var2) { if (var2 < 0L) { throw new IllegalArgumentException("Negative delay."); } else { this.sched(var1, System.currentTimeMillis() + var2, 0L); } } ...... private void sched(TimerTask var1, long var2, long var4) { if (var2 < 0L) { throw new IllegalArgumentException("Illegal execution time."); } else { if (Math.abs(var4) > 4611686018427387903L) { var4 >>= 1; } synchronized(this.queue) { if (!this.thread.newTasksMayBeScheduled) { throw new IllegalStateException("Timer already cancelled."); } else { synchronized(var1.lock) { if (var1.state != 0) { throw new IllegalStateException("Task already scheduled or cancelled"); } var1.nextExecutionTime = var2; var1.period = var4; var1.state = 1; } this.queue.add(var1); if (this.queue.getMin() == var1) { this.queue.notify(); } } } } } public void cancel() { synchronized(this.queue) { this.thread.newTasksMayBeScheduled = false; this.queue.clear(); this.queue.notify(); } } public int purge() { int var1 = 0; synchronized(this.queue) { for(int var3 = this.queue.size(); var3 > 0; --var3) { if (this.queue.get(var3).state == 3) { this.queue.quickRemove(var3); ++var1; } } if (var1 != 0) { this.queue.heapify(); } return var1; } } }
從中可以看到,不管是什么樣的任務都被加到一個queue隊列中順序執行,可以把這個隊列中的所有對象當成“同事”,同事之間的通信都是通過Timer來協調完成的,Timer就承擔了中介者的角色。
四、總結
優點:
- 減少類間依賴,將多對多依賴轉化成了一對多,降低了類間耦合;
- 類間各司其職,符合迪米特法則。
缺點:
中介者模式中將原本多個對象直接的相互依賴變成了中介者和多個同事類的依賴關系。當同事類越多時,中介者就會越臃腫,變得復雜且難以維護。
補充:服務治理用的也是這個思路,將多條鏈路的訪問進行拉平,調用同一個服務中心調用
git源碼:https://gitee.com/TongHuaShuShuoWoDeJieJu/design_pattern.git