【设计模式系列17】中介者模式原理及其在JDK源码中的体现

中介者模式原理及示例分析

  • 设计模式系列总览
  • 前言
  • 什么是中介者模式
  • 为什么需要中介者模式
  • 中介者模式示例
  • 中介者模式角色
  • 中介者模式和桥接模式的区别
  • 中介者模式在JDK源码中应用
  • 中介者模式应用场景
  • 中介者模式优缺点
  • 总结

设计模式系列总览

设计模式 飞机票
三大工厂模式 登机入口
策略模式 登机入口
委派模式 登机入口
模板方法模式 登机入口
观察者模式 登机入口
单例模式 登机入口
原型模式 登机入口
代理模式 登机入口
装饰者模式 登机入口
适配器模式 登机入口
建造者模式 登机入口
责任链模式 登机入口
享元模式 登机入口
组合模式 登机入口
门面模式 登机入口
桥接模式 登机入口
中介者模式 登机入口
迭代器模式 登机入口
状态模式 登机入口
解释器模式 登机入口
备忘录模式 登机入口
命令模式 登机入口
访问者模式 登机入口
软件设计7大原则和设计模式总结 登机入口

前言

本文主要介绍中介者模式的原理,并会结合示例进行分析。中介者模式其实和桥接有点相似,桥接模式也是通过组合来实现的,但是中介者模式和桥接模式的侧重点不一样,接下来就让我们一起来看看中介者模式吧。

什么是中介者模式

中介者模式(Mediator Pattern)又称为调节者模式或者调停者模式,是用来降低多个对象和类之间的通信复杂性。

中介者模式中用一个中介对象封装一系列的对象交互,从而使各个对象不需要显示的相互作用,达到松耦合的目的,使得维护更加容易。当某些对象之间的作用发生改变时,不会立刻影响其他的一些对象之间的作用,保证了对象之间的相互作用可以独立的变化。

中介者模式属于行为型模式,其核心思想是通过中介者对象解耦各层次对象的直接耦合,层次对象的对外依赖通信全部交由中介者转发。

为什么需要中介者模式

我们以微服务为例,微服务刚开始诞生的时候是下面这个现象:
【设计模式系列17】中介者模式原理及其在JDK源码中的体现_第1张图片
各个微服务之间互相依赖,整个依赖关系形成了网状,关系错综复杂,非常难以管理。所以后面就有了注册中心,注册中心就可以理解为一个中介者:
【设计模式系列17】中介者模式原理及其在JDK源码中的体现_第2张图片
可以看到,所有的微服务都将自己的信息发送到注册中心进行统一管理,避免了各个微服务之间各自依赖的乱像。

中介者模式示例

好了,装逼时刻又到了:Talk is cheap,Show you the code,先看一个非常简单的例子。
我们就以上面的注册中心为例子来写一个示例:

1、首先建立一个抽象的服务类,当然这里其实可以不用抽象类,为了方便功能扩展一般我们都是建议面向抽象编程:

package com.zwx.design.pattern.mediator;

public abstract class AbstractService {
     
    protected AbstractMediator mediator;

    public AbstractService(AbstractMediator mediator) {
     
        this.mediator = mediator;
    }

    public abstract void doSomething();
}

注意这里面持有了一个抽象中介者对象的引用,稍后我们就会建立中介者对象

2、然后新建两个具体的服务A和B来继承抽象服务类:

package com.zwx.design.pattern.mediator;

public class ServiceA extends AbstractService {
     

    public ServiceA(AbstractMediator mediator) {
     
        super(mediator);
    }

    @Override
    public void doSomething() {
     
        System.out.println("I'm Service A");
    }

    public void callB(){
     
        this.doSomething();
        System.out.println("调用Service B");
        mediator.callServiceB();
    }
}
package com.zwx.design.pattern.mediator;

public class ServiceB extends AbstractService {
     

    public ServiceB(AbstractMediator mediator) {
     
        super(mediator);
    }

    @Override
    public void doSomething() {
     
        System.out.println("I'm Service B");
    }

    public void callA(){
     
        this.doSomething();
        System.out.println("调用Service A");
        mediator.callServiceA();
    }
}

注意上面这两个类,除了重写了抽象方法doSomething,另外各自有一个调用对方服务的方法,如果说我们不使用中介者对象来实现,那么A就必须要持有B,而B又要持有A,服务一多,调用关系就会呈现上面的网状形式。而如果有了中介者对象就不一样了,这些服务就可以全部交由中介者来统一管理。

3、建立一个抽象中介者对象:

package com.zwx.design.pattern.mediator;

public abstract class AbstractMediator {
     
    protected AbstractService serviceA;
    protected AbstractService serviceB;

    public void setServiceA(AbstractService serviceA) {
     
        this.serviceA = serviceA;
    }

    public void setServiceB(AbstractService serviceB) {
     
        this.serviceB = serviceB;
    }

    public abstract void callServiceA();

    public abstract void callServiceB();
}

4、再建立一个具体的中介者对象:

package com.zwx.design.pattern.mediator;

public class RegisterCenter extends AbstractMediator{
     
    @Override
    public void callServiceA() {
     
        super.serviceA.doSomething();
    }

    @Override
    public void callServiceB() {
     
        super.serviceB.doSomething();
    }
}

5、最后我们建立一个测试类来测试一下:

package com.zwx.design.pattern.mediator;

public class TestMediator {
     
    public static void main(String[] args) {
     
        AbstractMediator registerCenter = new RegisterCenter();
        ServiceA serviceA = new ServiceA(registerCenter);
        ServiceB serviceB = new ServiceB(registerCenter);

        registerCenter.setServiceA(serviceA);
        registerCenter.setServiceB(serviceB);

        serviceA.callB();
        System.out.println("--------------------");
        serviceB.callA();
    }
}

输出结果如下:

I'm Service A
调用Service B
I'm Service B
--------------------
I'm Service B
调用Service A
I'm Service A

可以看到,在这个例子中我们实现了ServiceA和ServiceB的互相调用,但是相互都不持有对方的引用,而是通过一个中介者对象来统一管理,如果后续需要新增其他服务的调用,那么只需要改变中介者对象就可以了。

中介者模式角色

从上面示例中,我们可以得出中介者模式主要有4个角色:

  • 抽象中介者(Mediator):定义一个抽象角色,用于各个同事角色之间的通信(如示例中的AbstractMediator)。
  • 具体中介者(ConcreteMediator):从具体同事的对象接收消息,并像具体同事对象发出命令,用来协调各个同事对象之间通信协作(如示例中的RegisterCenter)。
  • 抽象同事类(Colleague):每一个同事对象均需要依赖中介者角色,所以一般将中介者角色集成到该角色之中,与其他同事在通信时,通过中介者角色进行转发(如示例中的AbstractService)。
  • 具体同事类(ConcreteColleague):负责实现自发行为(Self-Method),转发依赖方法(Dep-Method)交由中介者进行协调处理(如示例中的ServiceA和ServiceB,自发行为就是方法doSomething方法,转发行为对应callA和callB方法)。

中介者模式和桥接模式的区别

上一篇我们分析了桥接模式,看起来和这里的中介者模式有点相似的感觉。其实这两种设计模式的思想是一样的,这两种模式的侧重点和应用场景不一致。

桥接模式侧重的是将抽象和具体进行解耦,也就是一般就是特指2个维度,一个抽象,一个具体,比如桥接模式示例中的消息类型(邮箱消息,SMS消息)这就是抽象维度,而消息的紧急程度(如普通消息,紧急消息)这就是具体维度,然后将这两个维度通过一个中间对象(抽象角色)进行关联。

中介者模式侧重的是将多个相同类型对象之间的关联关系进行统一管理。

中介者模式在JDK源码中应用

我们打开JDK源码中的Timer类。我们任意查看其中的几个schedule方法:
【设计模式系列17】中介者模式原理及其在JDK源码中的体现_第3张图片
可以看到都调用了sched方法,那么我们再来看看sched方法:
【设计模式系列17】中介者模式原理及其在JDK源码中的体现_第4张图片
可以看到,所有的task都放入了Timer类中维护的task队列中。所以Timer就是充当了一个中介者的角色,而task队列内的任务就是具体同事对象。

中介者模式应用场景

中介者模式在我们日常生活中非常常见,比如租房中介,买房中介等,还有像我们的群聊,聊天室等等都有中介者的身影。中介者模式主要适用于以下场景:

  • 1、系统中的对象存在非常复杂的引用关系,而且相互依赖,导致依赖关系非常复杂时。
  • 2、当我们想要使用多个类之间的行为时,为了避免过多的使用继承,可以考虑使用中介者来实现。

中介者模式优缺点

优点:

  • 1、减少了类之间的相互依赖性,将多对多依赖转化成为了一对多依赖,降低了类间的耦合。
  • 2、使用了中介者模式之后,各个类都各司其职,符合了迪米特法则。

缺点

  • 1、当我们的同事角色非常多的时候,中介者对象需要维护非常多的同事角色对象,会使得中介者变得非常庞大,导致难以维护。

总结

本文主要介绍了中介者模式的定义并解释了为什么我们需要一个中介者,并通过一个简单的示例来帮助更好的理解中介者模式的思想,最后介绍了中介者模式在JDK源码中的体现。

请关注我,和孤狼一起学习进步

你可能感兴趣的:(设计模式,中介者模式,java,设计模式,桥接模式)