設(shè)計(jì)模式系列 — 中介者模式

點(diǎn)贊再看厚掷,養(yǎng)成習(xí)慣,公眾號(hào)搜一搜【一角錢技術(shù)】關(guān)注更多原創(chuàng)技術(shù)文章。本文 GitHub org_hejianhui/JavaStudy 已收錄膝晾,有我的系列文章亩冬。

前言

23種設(shè)計(jì)模式快速記憶的請(qǐng)看上面第一篇,本篇和大家一起來學(xué)習(xí)中介者模式相關(guān)內(nèi)容公条。

模式定義

定義一個(gè)中介對(duì)象來封裝一系列對(duì)象之間的交互拇囊,使原有對(duì)象之間的耦合松散,且可以獨(dú)立地改變它們之間的交互靶橱。中介者模式又叫調(diào)停模式寥袭,它是迪米特法則的典型應(yīng)用。

迪米特法則(Law of Demeter关霸,LoD)又叫作最少知識(shí)原則(Least Knowledge Principle传黄,LKP),產(chǎn)生于 1987 年美國東北大學(xué)(Northeastern University)的一個(gè)名為迪米特(Demeter)的研究項(xiàng)目队寇,由伊恩·荷蘭(Ian Holland)提出膘掰,被 UML 創(chuàng)始者之一的布奇(Booch)普及,后來又因?yàn)樵诮?jīng)典著作《程序員修煉之道》(The Pragmatic Programmer)提及而廣為人知佳遣。

迪米特法則的定義是:只與你的直接朋友交談识埋,不跟“陌生人”說話(Talk only to your immediate friends and not to strangers)凡伊。其含義是:如果兩個(gè)軟件實(shí)體無須直接通信,那么就不應(yīng)當(dāng)發(fā)生直接的相互調(diào)用窒舟,可以通過第三方轉(zhuǎn)發(fā)該調(diào)用系忙。其目的是降低類之間的耦合度,提高模塊的相對(duì)獨(dú)立性辜纲。

迪米特法則中的“朋友”是指:當(dāng)前對(duì)象本身笨觅、當(dāng)前對(duì)象的成員對(duì)象、當(dāng)前對(duì)象所創(chuàng)建的對(duì)象耕腾、當(dāng)前對(duì)象的方法參數(shù)等见剩,這些對(duì)象同當(dāng)前對(duì)象存在關(guān)聯(lián)、聚合或組合關(guān)系扫俺,可以直接訪問這些對(duì)象的方法苍苞。

模板實(shí)現(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收到請(qǐng)求。");
    }

    public void send() {
        System.out.println("具體同事類1發(fā)出請(qǐng)求狼纬。");
        mediator.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}

//具體同事類
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具體同事類2收到請(qǐng)求羹呵。");
    }

    public void send() {
        System.out.println("具體同事類2發(fā)出請(qǐng)求。");
        mediator.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}

結(jié)果實(shí)現(xiàn)如下

具體同事類1發(fā)出請(qǐng)求疗琉。
具體同事類2收到請(qǐng)求冈欢。
==============
具體同事類2發(fā)出請(qǐng)求。
具體同事類1收到請(qǐng)求盈简。

解決的問題

對(duì)象與對(duì)象之間存在大量的關(guān)聯(lián)關(guān)系凑耻,這樣勢(shì)必會(huì)導(dǎo)致系統(tǒng)的結(jié)構(gòu)變得很復(fù)雜,同時(shí)若一個(gè)對(duì)象發(fā)生改變柠贤,我們也需要跟蹤與之相關(guān)聯(lián)的對(duì)象香浩,同時(shí)做出相應(yīng)的處理。

模式組成

中介者模式實(shí)現(xiàn)的關(guān)鍵是找出“中介者”臼勉。

組成(角色) 作用
抽象中介者(Mediator)角色 它是中介者的接口邻吭,提供了同事對(duì)象注冊(cè)與轉(zhuǎn)發(fā)同事對(duì)象信息的抽象方法。
具體中介者(ConcreteMediator)角色 實(shí)現(xiàn)中介者接口宴霸,定義一個(gè) List 來管理同事對(duì)象囱晴,協(xié)調(diào)各個(gè)同事角色之間的交互關(guān)系,因此它依賴于同事角色瓢谢。
抽象同事類(Colleague)角色 定義同事類的接口畸写,保存中介者對(duì)象,提供同事對(duì)象交互的抽象方法恩闻,實(shí)現(xiàn)所有相互影響的同事類的公共功能艺糜。
具體同事類(Concrete Colleague)角色 是抽象同事類的實(shí)現(xiàn)者剧董,當(dāng)需要與其他同事對(duì)象交互時(shí)幢尚,由中介者對(duì)象負(fù)責(zé)后續(xù)的交互破停。

實(shí)例說明

實(shí)例概況

用中介者模式編寫一個(gè)“北京房地產(chǎn)交流平臺(tái)”程序。

分析:北京房地產(chǎn)交流平臺(tái)是“房地產(chǎn)中介公司”提供給“賣方客戶”與“買方客戶”進(jìn)行信息交流的平臺(tái)尉剩,比較適合用中介者模式來實(shí)現(xiàn)真慢。

使用步驟

步驟1:定義一個(gè)中介公司(Medium)接口,它是抽象中介者理茎,它包含了客戶注冊(cè)方法 register(Customer member) 和信息轉(zhuǎn)發(fā)方法 relay(String from,String ad)黑界;

interface Medium {
    //客戶注冊(cè)
    void register(Customer member);

    //轉(zhuǎn)發(fā)
    void relay(String from, String ad);
}

步驟2:定義一個(gè)北京房地產(chǎn)中介(EstateMedium)公司,它是具體中介者類皂林,它包含了保存客戶信息的 List 對(duì)象朗鸠,并實(shí)現(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:定義一個(gè)客戶(Qistomer)類础倍,它是抽象同事類烛占,其中包含了中介者的對(duì)象,和發(fā)送信息的 send(String ad) 方法與接收信息的 receive(String from沟启,Stringad) 方法的接口忆家,由于本程序是窗體程序,所以本類繼承 JPmme 類德迹,并實(shí)現(xiàn)動(dòng)作事件的處理方法 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)類的子類卸例,它們實(shí)現(xiàn)了父類中的抽象方法,通過中介者類進(jì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動(dòng)條滾動(dòng)到最底端
        ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
        medium.relay(name, ad);
    }

    public void receive(String from, String ad) {
        ReceiveArea.append(from + "說: " + ad + "\n");
        //使?jié)L動(dòng)條滾動(dòng)到最底端
        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動(dòng)條滾動(dòng)到最底端
        ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
        medium.relay(name, ad);
    }

    public void receive(String from, String ad) {
        ReceiveArea.append(from + "說: " + ad + "\n");
        //使?jié)L動(dòng)條滾動(dòng)到最底端
        ReceiveArea.setCaretPosition(ReceiveArea.getText().length());
    }
}

輸出結(jié)果

優(yōu)點(diǎn)

  1. 降低了對(duì)象之間的耦合性币厕,使得對(duì)象易于獨(dú)立地被復(fù)用。
  2. 將對(duì)象間的一對(duì)多關(guān)聯(lián)轉(zhuǎn)變?yōu)橐粚?duì)一的關(guān)聯(lián)芽腾,提高系統(tǒng)的靈活性旦装,使得系統(tǒng)易于維護(hù)和擴(kuò)展。

缺點(diǎn)

當(dāng)同事類太多時(shí)摊滔,中介者的職責(zé)將很大阴绢,它會(huì)變得復(fù)雜而龐大,以至于系統(tǒng)難以維護(hù)艰躺。

應(yīng)用場(chǎng)景

  • 當(dāng)對(duì)象之間存在復(fù)雜的網(wǎng)狀結(jié)構(gòu)關(guān)系而導(dǎo)致依賴關(guān)系混亂且難以復(fù)用時(shí)呻袭。
  • 當(dāng)想創(chuàng)建一個(gè)運(yùn)行于多個(gè)類之間的對(duì)象,又不想生成新的子類時(shí)腺兴。

模式的擴(kuò)展

在實(shí)際開發(fā)中左电,通常采用以下兩種方法來簡(jiǎn)化中介者模式,使開發(fā)變得更簡(jiǎn)單。

  1. 不定義中介者接口篓足,把具體中介者對(duì)象實(shí)現(xiàn)成為單例段誊。
  2. 同事對(duì)象不持有中介者,而是在需要的時(shí)候直接獲取中介者對(duì)象并調(diào)用栈拖。


程序代碼如下

package com.niuh.designpattern.mediator.v3;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 簡(jiǎn)化中介者模式
 * </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();
    }
}

//簡(jiǎn)單單例中介者
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:收到請(qǐng)求连舍。");
    }

    public void send() {
        SimpleMediator smd = SimpleMediator.getMedium();
        System.out.println("具體同事類1:發(fā)出請(qǐng)求...");
        smd.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}

//具體同事類
class SimpleConcreteColleague2 implements SimpleColleague {
    SimpleConcreteColleague2() {
        SimpleMediator smd = SimpleMediator.getMedium();
        smd.register(this);
    }

    public void receive() {
        System.out.println("具體同事類2:收到請(qǐng)求。");
    }

    public void send() {
        SimpleMediator smd = SimpleMediator.getMedium();
        System.out.println("具體同事類2:發(fā)出請(qǐng)求...");
        smd.relay(this); //請(qǐng)中介者轉(zhuǎn)發(fā)
    }
}

輸出結(jié)果如下

具體同事類1:發(fā)出請(qǐng)求...
具體同事類2:收到請(qǐng)求涩哟。
==============
具體同事類2:發(fā)出請(qǐng)求...
具體同事類1:收到請(qǐng)求索赏。

源碼中的應(yīng)用

java.util.Timer
java.util.concurrent.Executer#execute()
java.util.concurrent.ExecuterService#submit()
java.lang.reflect.Method#invoke()

PS:以上代碼提交在 Githubhttps://github.com/Niuh-Study/niuh-designpatterns.git

文章持續(xù)更新,可以公眾號(hào)搜一搜「 一角錢技術(shù) 」第一時(shí)間閱讀贴彼, 本文 GitHub org_hejianhui/JavaStudy 已經(jīng)收錄潜腻,歡迎 Star。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末器仗,一起剝皮案震驚了整個(gè)濱河市砾赔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌青灼,老刑警劉巖暴心,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異杂拨,居然都是意外死亡专普,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門弹沽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檀夹,“玉大人,你說我怎么就攤上這事策橘≌ǘ桑” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵丽已,是天一觀的道長蚌堵。 經(jīng)常有香客問我,道長沛婴,這世上最難降的妖魔是什么吼畏? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮嘁灯,結(jié)果婚禮上泻蚊,老公的妹妹穿的比我還像新娘。我一直安慰自己丑婿,他們只是感情好性雄,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布没卸。 她就那樣靜靜地躺著,像睡著了一般秒旋。 火紅的嫁衣襯著肌膚如雪办悟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天滩褥,我揣著相機(jī)與錄音,去河邊找鬼炫加。 笑死瑰煎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俗孝。 我是一名探鬼主播酒甸,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赋铝!你這毒婦竟也來了插勤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤革骨,失蹤者是張志新(化名)和其女友劉穎农尖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體良哲,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盛卡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筑凫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滑沧。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巍实,靈堂內(nèi)的尸體忽然破棺而出滓技,到底是詐尸還是另有隱情,我是刑警寧澤棚潦,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布令漂,位于F島的核電站,受9級(jí)特大地震影響丸边,放射性物質(zhì)發(fā)生泄漏洗显。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一原环、第九天 我趴在偏房一處隱蔽的房頂上張望挠唆。 院中可真熱鬧,春花似錦嘱吗、人聲如沸玄组。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俄讹。三九已至哆致,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間患膛,已是汗流浹背摊阀。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踪蹬,地道東北人胞此。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像跃捣,于是被迫代替她去往敵國和親漱牵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容