Java设计模式-中介者模式

https://www.shengchulai.com/blog-CiTs5E8tUf.htm

前言

小时候钟爱战争片,《地道战》、《鸡毛信》、《铁道游击队》一系列的老电影,咦~想起都激动得起鸡皮疙瘩。不过觉得特别逗的是,电影里面总会有“这里是xxx,我们被包围了,请求支援请求支援”这么一句台词。

来分析一下这句台词怎么来的。假设有N多个战区,战区的分布错综复杂,很多时候一个战区的丢失会影响整个战争局势。所以这就得要有一个司令部指挥和协调各个战区,而一旦战区被攻打,报告司令部请求支援,司令部则调度其他战区进行协助。

那在我们的程序设计中有没有这样的模式?有的,中介者模式应运而生,目的就是处理这样的情景问题。

中介者模式

定义

中介者封装一系列对象相互作用,使得这些对象耦合松散,并且可以独立的改变它们之间的交互。

UML

中介者模式涉及到的角色有四个:

  • 抽象中介者角色:抽象中介者角色定义统一的接口,以及一个或者多个事件方法,用于各同事角色之间的通信。

  • 具体中介者角色:实现了抽象中介者所声明的事件方法,协调各同事类之间的行为,持有所有同事类对象的引用。

  • 抽象同事类角色:定义了抽象同事类,持有抽象中介者对象的引用。

  • 具体同事类角色:继承抽象同事类,实现自己业务,通过中介者跟其他同事类进行通信。

中介者模式实战

假设这样的一个情景。有A、B、C三个战区,A被敌方攻打,请求B支援。但是此时B也被敌方攻打,所以A继续向C请求支援,这么巧C此时正在支援B。情景比较简单,我们的例子也围绕着这个情景来展开,首先来看不使用中介者模式是怎么实现的。

A战区代码如下:

public class SituationA {    // 请求支援    public void requestSupport(String situation) {        System.out.println(getClass().getSimpleName() +             ":这里是A战区,现在被敌方攻打,请求" + situation + "支援");    }}

SituationA定义了请求支援的方法,向其他战区请求支援。再来看B战区的代码定义:

public class SituationB {    // 请求支援    public void requestSupport(String situation) {        System.out.println(getClass().getSimpleName() + ":这里是B战区,现在被敌方攻打,请求" + situation + "支援");    }

    // 是否支援    public void support(boolean isSupport) {        if (isSupport) {            System.out.println(getClass().getSimpleName() + ":Copy that,还有五秒钟到达战场");        } else {            System.out.println(getClass().getSimpleName() + ":支援你妹,我也正在被攻打");        }    }}

SituationB也定义了请求支援的方法,还多了根据isSupport是否支援其他战区的方法。还有SituationC,SituationC和SituationB代码差不多,直接贴出来了,不多做解释。

public class SituationC {    // 请求支援    public void requestSupport(String situation) {        System.out.println(getClass().getSimpleName() + ":这里是B战区,现在被敌方攻打,请求" + situation + "支援");    }

    // 是否支援    public void support(boolean isSupport) {        if (isSupport) {            System.out.println(getClass().getSimpleName() + ":Copy that,还有五秒钟到达战场");        } else {            System.out.println(getClass().getSimpleName() + ":不好意思,来迟一步了,正在前往别的战区支援");        }    }}

OK,三个类都定义好了,我们根据情景看看客户端是怎样运行的,代码如下:

public class Client {    public static void main(String[] args) {        System.out.println("-------A被攻打,请求B支援--------");        SituationA situationA = new SituationA();        situationA.requestSupport("B");        System.out.println("-------B也正在被攻打--------");        SituationB situationB = new SituationB();        situationB.support(false);        System.out.println("-------A又向C请求支援--------");        situationA.requestSupport("C");        System.out.println("-------C很忙--------");        SituationC situationC = new SituationC();        situationC.support(false);    }}

运行结果如下:

-------A被攻打,请求B支援--------

SituationA:这里是A战区,现在被敌方攻打,请求B支援

-------B也正在被攻打--------

SituationB:支援你妹,我也正在被攻打

-------A又向C请求支援--------

SituationA:这里是A战区,现在被敌方攻打,请求C支援

-------C很忙--------

SituationC:不好意思,来迟一步了,正在前往别的战区支援

回到我们的场景当中,A、B、C是相互两两关联的,并且关联的两个类与其他类是不能协调通信。因此,在实际中,战区类增多,它们之间的耦合度越高,这样首先会造成当一个类修改了,其他类也要跟着需要修改,然后就是多个类之间的通信变得复杂混乱。就跟我们上面列子一样,A、B、C相互支援,A并不知道C已经支援B了。因为这些原因,在代码设计中加入中介者角色,每个类都经过中介者进行沟通和协调。

下面来看中介者模式的实现,首先定义抽象中介者角色类,代码如下:

public abstract class Mediator {    protected SituationA situationA;    protected SituationB situationB;    protected SituationC situationC;

    Mediator() {        situationA = new SituationA(this);        situationB = new SituationB(this);        situationC = new SituationC(this);    }

    /**     * 事件的业务流程处理     *     * @param method     */    public abstract void execute(String method);}

抽象中介者类主要定义了同事类的事件业务流程方法,并且持有每一个具体同事类的引用,再来看具体中介者的实现:

public class Command extends Mediator {

    public void execute(String method) {        if (method.equals("aRequestSupport")) {            this.aRequestSupport();        } else if (method.equals("bRequestSupport")) {            this.bRequestSupport();        }    }

    // A请求支援    private void aRequestSupport() {        System.out.println("SituationA:这里是A战区,现在被敌方攻打,请求支援");        boolean isBSupport = isSupport();  // B是否可以支援        super.situationB.bSupport(isBSupport);        if (!isBSupport) { // B支援不了,请求C            System.out.println("-------A又向C请求支援--------");            boolean isASupport = isSupport();  // B是否可以支援            super.situationC.cSupport(isASupport);            if (!isASupport) {                System.out.println("-------自己看着办吧。--------");            }        }    }

    // B请求支援    public void bRequestSupport() {        System.out.println("这里是B的请求支援");    }

    private boolean isSupport() {        Random rand = new Random();        return rand.nextBoolean();    }}

代码比较长,但也比较简单。定义了处理各个对象关系的业务方法,把依赖关系转移到了这个业务方法中,而同事类只需要委托中介者协调各个同事类的业务逻辑。

public abstract class Colleague {    protected Mediator mediator;

    public Colleague(Mediator mediator) {        this.mediator = mediator;    }}

很简单的就一个构造方法,继续看具体同事类的实现,我先把各个同事类的代码都贴出来:

// A战区public class SituationA extends Colleague {

    public SituationA(Mediator mediator) {        super(mediator);    }

    // 请求支援    public void aRequestSupport() {        super.mediator.execute("aRequestSupport");    }}

// B战区public class SituationB extends Colleague {

    public SituationB(Mediator mediator) {        super(mediator);    }

    // 请求支援    public void bRequestSupport() {        super.mediator.execute("bRequestSupport");    }

    public void bSupport(boolean isSupport) {        if (isSupport) {            System.out.println("SituationB:Copy that,还有五秒钟到达战场");        } else {            System.out.println("-------B也正在被攻打--------");            System.out.println("SituationB:支援你妹,我也正在被攻打");        }    }}

// C战区public class SituationC extends Colleague {    public SituationC(Mediator mediator) {        super(mediator);    }

    // 请求支援    public void cRequestSupport() {        super.mediator.execute("cRequestSupport");    }

    public void cSupport(boolean isSupport) {        if (isSupport) {            System.out.println(getClass().getSimpleName() + ":Copy that,还有五秒钟到达战场");        } else {            System.out.println(getClass().getSimpleName() + ":不好意思,来迟一步了,正在前往别的战区支援");        }    }}

跟前面说的一样,通过cRequestSupport方法中的execute委托中介者处理同事类的业务逻辑,本身只负责处理自身的业务。

最后来看客户端的实现,代码如下:

public class Client {    public static void main(String[] args) {        Mediator mediator = new Command();        System.out.println("-------A被攻打,请求支援--------");        SituationA situationA = new SituationA(mediator);        situationA.aRequestSupport();    }}

可以看到,表面上请求还是从A发出,但是A已经委托了中介者进行业务逻辑和流程的处理。这样的好处就是每个同事类的职责都很清晰,跟其他同事类有关联的都委托到中介者,本身专注自己的行为。

运行客户端,结果如下:

-------A被攻打,请求支援--------

SituationA:这里是A战区,现在被敌方攻打,请求支援

-------B也正在被攻打--------

SituationB:支援你妹,我也正在被攻打

-------A又向C请求支援--------

SituationC:Copy that,还有五秒钟到达战场

优缺点

优点

解耦。把同事类原来一对多的依赖变成一对一的依赖,降低同事类的耦合度,同时也符合了迪米特原则。

缺点

1)中介者模式把业务流程和协调都写在中介者,当同事类越多,中介者的业务就越复杂,造成不好管理的弊端。

2)中介者模式还有一个明显的缺点,如果要增减同事类,必须得修改抽象中介者角色和具体中介者角色类。

模式扩展

中介者模式和观察者模式混编

为什么要跟观察者模式组合混编?首先,上面提到了如果要增加或者删除同事类,必须对中介者这个角色进行修改,因为中介者角色的业务逻辑相对比较集中和复杂,修改中介者角色会比较麻烦。另外一点是,使用观察者模式实现同事类(被观察者)的通信可以优化中介者的业务逻辑流程,避免过多使用if...else。

同事类通知->中介者协调处理->中介者通知同事类

其实可以说成中介者模式是通过观察者模式实现的,都是事件驱动模型。这里简单阐述下原理,把中介者作为观察者,即中介者角色实现Observer接口,重写update方法(重点就在update,同事类跟中介者,中介者月同事类之间的通信就在这实现)。同事类继承Observable被观察者类,通过notifyObservers可以与中介者通信。这样就在相当于观察者模式的基础上(观察者模式的交互路径较短),在中介者中增加了消息转发的功能,也就是说同事类之间的通信经过了中介者。

中介者模式VS门面模式

先简单介绍下门面模式。要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,这就是门面模式。门面模式主要的是提供了一个高层次的接口,也就是所谓的一个统一对象,通过它跟子系统进行通信。这样做的好处就是,第一,外部与系统内部解耦,减少相互之间的依赖;第二,增加系统内部的灵活性,系统内部变化不影响外部跟门面角色的关系。比如拍照,可以用手机拍,也可以用单反相机拍,把手机和单反封装在一起,外部只能看到一个摄像头,你只能说拍照,里面到底是手机拍还是单反拍是不知道的。

中介者模式和门面模式同样的都是通过封装一个角色来进行隔离解耦,但中介者强调的是中介协调同事类之间的通信,门面模式则是通过门面对内部进行隔离。另外,中介者模式的通信是双向的,而门面模式的通信是单向的。

总结

系统中多个对象之间相互依赖,并且依赖关系结构复杂导致对象之间的耦合度增大,修改难度大,这个时候可以考虑使用中介者模式来梳理对象之间的通信,达到降低对象之间耦合度的效果。中介者模式就到这,下一篇命令模式,您的点赞和关注是我的动力,钦敬钦敬!

设计模式Java源码GitHub下载:

(0)

相关推荐

  • MediatorPattern中介者模式

    中介者模式 1.定义 使用一个中介对象封装一系列的对象交互,中介者使用对象不需要显式的相互作用,从而实降现低耦合度,且可以独立改变被封装的一些列对象之间的交互. 中介者模式也叫做调停者模式:对象之间的 ...

  • PHP设计模式之中介者模式

    PHP设计模式之中介者模式 上回说道,我们在外打工的经常会和一类人有很深的接触,那就是房产中介.大学毕业后马上就能在喜欢的城市买到房子的X二代不在我们的考虑范围内哈.既然需要长期的租房,那么因为工作或 ...

  • 通俗易懂设计模式解析——中介者模式

    前言 今天我们一起看看中介者模式,怎么去理解这个模式呢?说起来也简单.好理解.生活中我们租房经常都是通过中介来实现的.一般租房要么是房东直租要么是中介.那么今天要讲的中介者模式和租房的这个中介是否有关 ...

  • 行为型模式之中介者模式

    在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是"网状结构",它要求每个对象都必须知道它需要交互的对象. 如果把这种"网状结构"改为& ...

  • 图解Java设计模式之中介者模式

    智能家庭项目 传统方案解决智能家庭管理问题 中介者模式基本介绍 中介者模式的原理类图 中介者模式 - 智能家庭的操作流程 中介者模式的注意事项和细节 智能家庭项目 1)智能家庭包括各种设备,闹钟.咖啡 ...

  • 设计模式------中介者模式

    中介者模式: 定义一个对象来封装一系列对象的交互. 中介者的四个角色: 1.Mediator(抽象中介者) 2.ConcreteMediator(具体中介者) 3.Colleague(抽象同事类) 4 ...

  • Java设计模式【命令模式】

    命令模式 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行.这个过程好在,三者相互解耦,任何一方都不 ...

  • 图解Java设计模式之解释器模式

    图解Java设计模式之解释器模式 四则运算问题 传统方案解决四则运算问题分析 解释器模式基本介绍 解释器模式来实现四则 解析器模式在Spring框架中的源码分析 解释器模式的注意事项和细节 四则运算问 ...

  • 图解Java设计模式之备忘录模式

    游戏角色状态恢复问题 传统方案解决游戏角色恢复 传统的方式的问题分析 备忘录模式基本介绍 备忘录模式原理类图 游戏角色恢复状态实例 备忘录模式的注意事项和细节 游戏角色状态恢复问题 游戏角色有攻击力和 ...

  • 图解Java设计模式之命令模式

    智能生活项目需求 命令模式基本介绍 命令模式的原理类图 命令模式在Spring框架中JdbcTemplate应用源码分析 命令模式的注意事项和细节 智能生活项目需求 看一个具体的需求 我们买类一套智能 ...

  • 图解Java设计模式之模板模式

    豆浆制作问题 模板方法模式基本介绍 模板方法模式原理类图 模板方法模式解决豆浆制作问题 模板方法模式的钩子方法 模板方法模式在Spring框架中的源码分析 模板方法模式的注意事项和细节 豆浆制作问题 ...

  • 图解Java设计模式之桥接模式

    手机操作问题 传统方案解决手机操作问题 传统方案解决手机操作问题分析 桥接模式(Bridge)-基本介绍 桥接模式解决手机操作问题 桥接模式在JDBC中的源码解析 桥接模式的注意事项和细节 桥接模式其 ...

  • java设计模式7——桥接模式

    java设计模式7--桥接模式 1.桥接模式介绍 桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立的变化.它是一种对象结构型模式,又称为柄体模式或接口模式. 2.解决问题 2.1.将复杂的组合 ...

  • java设计模式2————工厂模式

    java设计模式2————工厂模式