設計模式系列—中介者模式
模式定義
定義一個中介對象來封裝一系列對象之間的交互,使原有對象之間的耦合松散,且可以獨立地改變它們之間的交互。中介者模式又叫調(diào)停模式,它是迪米特法則的典型應用。
- 迪米特法則(Law of Demeter,LoD)又叫作最少知識原則(Least Knowledge Principle,LKP),產(chǎn)生于 1987 年美國東北大學(Northeastern University)的一個名為迪米特(Demeter)的研究項目,由伊恩·荷蘭(Ian Holland)提出,被 UML 創(chuàng)始者之一的布奇(Booch)普及,后來又因為在經(jīng)典著作《程序員修煉之道》(The Pragmatic Programmer)提及而廣為人知。
- 迪米特法則的定義是:只與你的直接朋友交談,不跟“陌生人”說話(Talk only to your immediate friends and not to strangers)。其含義是:如果兩個軟件實體無須直接通信,那么就不應當發(fā)生直接的相互調(diào)用,可以通過第三方轉(zhuǎn)發(fā)該調(diào)用。其目的是降低類之間的耦合度,提高模塊的相對獨立性。
- 迪米特法則中的“朋友”是指:當前對象本身、當前對象的成員對象、當前對象所創(chuàng)建的對象、當前對象的方法參數(shù)等,這些對象同當前對象存在關聯(lián)、聚合或組合關系,可以直接訪問這些對象的方法。
模板實現(xiàn)如下:
- package com.niuh.designpattern.mediator.v1;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * <p>
- * 中介者模式
- * </p>
- */
- public class MediatorPattern {
- public static void main(String[] args) {
- Mediator md = new ConcreteMediator();
- Colleague c1, c2;
- c1 = new ConcreteColleague1();
- c2 = new ConcreteColleague2();
- md.register(c1);
- md.register(c2);
- c1.send();
- System.out.println("==============");
- c2.send();
- }
- }
- //抽象中介者
- abstract class Mediator {
- public abstract void register(Colleague colleague);
- public abstract void relay(Colleague cl); //轉(zhuǎn)發(fā)
- }
- //具體中介者
- class ConcreteMediator extends Mediator {
- private List<Colleague> colleagues = new ArrayList<Colleague>();
- public void register(Colleague colleague) {
- if (!colleagues.contains(colleague)) {
- colleagues.add(colleague);
- colleague.setMedium(this);
- }
- }
- public void relay(Colleague cl) {
- for (Colleague ob : colleagues) {
- if (!ob.equals(cl)) {
- ((Colleague) ob).receive();
- }
- }
- }
- }
- //抽象同事類
- abstract class Colleague {
- protected Mediator mediator;
- public void setMedium(Mediator mediator) {
- this.mediator = mediator;
- }
- public abstract void receive();
- public abstract void send();
- }
- //具體同事類
- class ConcreteColleague1 extends Colleague {
- public void receive() {
- System.out.println("具體同事類1收到請求。");
- }
- public void send() {
- System.out.println("具體同事類1發(fā)出請求。");
- mediator.relay(this); //請中介者轉(zhuǎn)發(fā)
- }
- }
- //具體同事類
- class ConcreteColleague2 extends Colleague {
- public void receive() {
- System.out.println("具體同事類2收到請求。");
- }
- public void send() {
- System.out.println("具體同事類2發(fā)出請求。");
- mediator.relay(this); //請中介者轉(zhuǎn)發(fā)
- }
- }
結(jié)果實現(xiàn)如下:
- 具體同事類1發(fā)出請求。
- 具體同事類2收到請求。
- 具體同事類2發(fā)出請求。
- 具體同事類1收到請求。
解決的問題
對象與對象之間存在大量的關聯(lián)關系,這樣勢必會導致系統(tǒng)的結(jié)構變得很復雜,同時若一個對象發(fā)生改變,我們也需要跟蹤與之相關聯(lián)的對象,同時做出相應的處理。
模式組成
中介者模式實現(xiàn)的關鍵是找出“中介者”。
實例說明
實例概況
用中介者模式編寫一個“北京房地產(chǎn)交流平臺”程序。
分析:北京房地產(chǎn)交流平臺是“房地產(chǎn)中介公司”提供給“賣方客戶”與“買方客戶”進行信息交流的平臺,比較適合用中介者模式來實現(xiàn)。
使用步驟
步驟1:定義一個中介公司(Medium)接口,它是抽象中介者,它包含了客戶注冊方法 register(Customer member) 和信息轉(zhuǎn)發(fā)方法 relay(String from,String ad);
- interface Medium {
- //客戶注冊
- void register(Customer member);
- //轉(zhuǎn)發(fā)
- void relay(String from, String ad);
- }
步驟2:定義一個北京房地產(chǎn)中介(EstateMedium)公司,它是具體中介者類,它包含了保存客戶信息的 List 對象,并實現(xiàn)了中介公司中的抽象方法。
- //具體中介者:房地產(chǎn)中介
- class EstateMedium implements Medium {
- private List<Customer> members = new ArrayList<Customer>();
- public void register(Customer member) {
- if (!members.contains(member)) {
- members.add(member);
- member.setMedium(this);
- }
- }
- public void relay(String from, String ad) {
- for (Customer ob : members) {
- String name = ob.getName();
- if (!name.equals(from)) {
- ((Customer) ob).receive(from, ad);
- }
- }
- }
- }
步驟3:定義一個客戶(Qistomer)類,它是抽象同事類,其中包含了中介者的對象,和發(fā)送信息的 send(String ad) 方法與接收信息的 receive(String from,Stringad) 方法的接口,由于本程序是窗體程序,所以本類繼承 JPmme 類,并實現(xiàn)動作事件的處理方法 actionPerformed(ActionEvent e)。
- //抽象同事類:客戶
- abstract class Customer extends JFrame implements ActionListener {
- private static final long serialVersionUID = -7219939540794786080L;
- protected Medium medium;
- protected String name;
- JTextField SentText;
- JTextArea ReceiveArea;
- public Customer(String name) {
- super(name);
- this.name = name;
- }
- void ClientWindow(int x, int y) {
- Container cp;
- JScrollPane sp;
- JPanel p1, p2;
- cp = this.getContentPane();
- SentText = new JTextField(18);
- ReceiveArea = new JTextArea(10, 18);
- ReceiveArea.setEditable(false);
- p1 = new JPanel();
- p1.setBorder(BorderFactory.createTitledBorder("接收內(nèi)容:"));
- p1.add(ReceiveArea);
- sp = new JScrollPane(p1);
- cp.add(sp, BorderLayout.NORTH);
- p2 = new JPanel();
- p2.setBorder(BorderFactory.createTitledBorder("發(fā)送內(nèi)容:"));
- p2.add(SentText);
- cp.add(p2, BorderLayout.SOUTH);
- SentText.addActionListener(this);
- this.setLocation(x, y);
- this.setSize(250, 330);
- this.setResizable(false); //窗口大小不可調(diào)整
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setVisible(true);
- }
- public void actionPerformed(ActionEvent e) {
- String tempInfo = SentText.getText().trim();
- SentText.setText("");
- this.send(tempInfo);
- }
- public String getName() {
- return name;
- }
- public void setMedium(Medium medium) {
- this.medium = medium;
- }
- public abstract void send(String ad);
- public abstract void receive(String from, String ad);
- }
步驟4:定義賣方(Seller)類和買方(Buyer)類,它們是具體同事類,是客戶(Customer)類的子類,它們實現(xiàn)了父類中的抽象方法,通過中介者類進行信息交流。
- //具體同事類:賣方
- class Seller extends Customer {
- private static final long serialVersionUID = -1443076716629516027L;
- public Seller(String name) {
- super(name);
- ClientWindow(50, 100);
- }
- public void send(String ad) {
- ReceiveArea.append("我(賣方)說: " + ad + "\n");
- //使?jié)L動條滾動到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- medium.relay(name, ad);
- }
- public void receive(String from, String ad) {
- ReceiveArea.append(from + "說: " + ad + "\n");
- //使?jié)L動條滾動到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- }
- }
- //具體同事類:買方
- class Buyer extends Customer {
- private static final long serialVersionUID = -474879276076308825L;
- public Buyer(String name) {
- super(name);
- ClientWindow(350, 100);
- }
- public void send(String ad) {
- ReceiveArea.append("我(買方)說: " + ad + "\n");
- //使?jié)L動條滾動到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- medium.relay(name, ad);
- }
- public void receive(String from, String ad) {
- ReceiveArea.append(from + "說: " + ad + "\n");
- //使?jié)L動條滾動到最底端
- ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
- }
- }
輸出結(jié)果
優(yōu)點
- 降低了對象之間的耦合性,使得對象易于獨立地被復用。
- 將對象間的一對多關聯(lián)轉(zhuǎn)變?yōu)橐粚σ坏年P聯(lián),提高系統(tǒng)的靈活性,使得系統(tǒng)易于維護和擴展。
缺點
當同事類太多時,中介者的職責將很大,它會變得復雜而龐大,以至于系統(tǒng)難以維護。
應用場景
- 當對象之間存在復雜的網(wǎng)狀結(jié)構關系而導致依賴關系混亂且難以復用時。
- 當想創(chuàng)建一個運行于多個類之間的對象,又不想生成新的子類時。
模式的擴展
在實際開發(fā)中,通常采用以下兩種方法來簡化中介者模式,使開發(fā)變得更簡單。
- 不定義中介者接口,把具體中介者對象實現(xiàn)成為單例。
- 同事對象不持有中介者,而是在需要的時候直接獲取中介者對象并調(diào)用。
程序代碼如下:
- package com.niuh.designpattern.mediator.v3;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * <p>
- * 簡化中介者模式
- * </p>
- */
- public class SimpleMediatorPattern {
- public static void main(String[] args) {
- SimpleColleague c1, c2;
- c1 = new SimpleConcreteColleague1();
- c2 = new SimpleConcreteColleague2();
- c1.send();
- System.out.println("==============");
- c2.send();
- }
- }
- //簡單單例中介者
- class SimpleMediator {
- private static SimpleMediator smd = new SimpleMediator();
- private List<SimpleColleague> colleagues = new ArrayList<SimpleColleague>();
- private SimpleMediator() {
- }
- public static SimpleMediator getMedium() {
- return (smd);
- }
- public void register(SimpleColleague colleague) {
- if (!colleagues.contains(colleague)) {
- colleagues.add(colleague);
- }
- }
- public void relay(SimpleColleague scl) {
- for (SimpleColleague ob : colleagues) {
- if (!ob.equals(scl)) {
- ((SimpleColleague) ob).receive();
- }
- }
- }
- }
- //抽象同事類
- interface SimpleColleague {
- void receive();
- void send();
- }
- //具體同事類
- class SimpleConcreteColleague1 implements SimpleColleague {
- SimpleConcreteColleague1() {
- SimpleMediator smd = SimpleMediator.getMedium();
- smd.register(this);
- }
- public void receive() {
- System.out.println("具體同事類1:收到請求。");
- }
- public void send() {
- SimpleMediator smd = SimpleMediator.getMedium();
- System.out.println("具體同事類1:發(fā)出請求...");
- smd.relay(this); //請中介者轉(zhuǎn)發(fā)
- }
- }
- //具體同事類
- class SimpleConcreteColleague2 implements SimpleColleague {
- SimpleConcreteColleague2() {
- SimpleMediator smd = SimpleMediator.getMedium();
- smd.register(this);
- }
- public void receive() {
- System.out.println("具體同事類2:收到請求。");
- }
- public void send() {
- SimpleMediator smd = SimpleMediator.getMedium();
- System.out.println("具體同事類2:發(fā)出請求...");
- smd.relay(this); //請中介者轉(zhuǎn)發(fā)
- }
- }
輸出結(jié)果如下:
- 具體同事類1:發(fā)出請求...
- 具體同事類2:收到請求。
- 具體同事類2:發(fā)出請求...
- 具體同事類1:收到請求。
源碼中的應用
- java.util.Timer
- java.util.concurrent.Executer#execute()
- java.util.concurrent.ExecuterService#submit()
- java.lang.reflect.Method#invoke()
PS:以上代碼提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git