跳到正文
返回

【设计模式】中介者模式

发表于 更新于
浏览量: --

封面来源:中介者设计模式 (refactoringguru.cn),如有侵权,请联系删除。

参考链接:

0. 前言

工作上的代码不说得心应手,也算是轻松自在。目前来说,较为难处理的是命名,一个直观、准确的命名总是让我纠结万分,抓破脑袋。

前些时候在项目中发现了一个以 Mediator 结尾的类,很显然这个类承担了某些职责。

Mediator 可被翻译为「调解人」,在计算机术语中,常被翻译为「中介者」,与设计模式中的 中介者模式 相关。

1. 模式的定义与特点

当有多个飞行器需要降落在机场时,这些飞行器驾驶员之间并不会直接沟通来决定降落的先后,他们会把自己的情况报告给控制塔台,由塔台指挥飞行器的降落先后。

这里的控制塔台扮演了 中介者 的角色,驾驶员之间不再直接沟通,所有的沟通通过控制塔台完成,如果没有控制塔台,恐怕难以完成一次安全的降落吧。

说到中介,更容易想到租房中介。通过租房中介,租房者与房东之间不直接商讨租房细节,这些细节由中介拟定并告知双方,双方同一后签订租房合同。对于房东而言,租房中介节约了房东的时间;对于租房者而言,租房中介提供了细节完善的合同,降低了租房者的担忧。这样一个「完美的」角色自然是不会白干活,中介会显式(或隐式)地向租房者收取一定的中介费,一般情况下,这份中介费是一个月的房租,因此租房中介会更站向房东,尤其是在房租定价方面,当然,这与本文的关系不大。

在实际项目中类与类之间往往存在复杂的引用依赖关系,为了降低它们之间的耦合关系,这时也需要一个 中介者,这个中介者描述了类与类之间复杂的引用依赖关系,使之不再显式地相互引用,既降低了耦合,也方便后续改变它们之间的引用与依赖。

定义

中介者(Mediator)模式:用一个中介对象来封装对象之间的交互,使对象之间不再显式地相互引用。

中介者模式 * 又被称为 * 调停者模式,是一种对象行为型模式。

优点

缺点

2. 适用场景

3. 模式的结构

中介者模式中包含 组件中介者 两个角色。

在面向接口编程的思想下,会定义组件和中介者的顶层接口,利用顶层接口的组合与关联完成组件之间的交互逻辑。

中介者模式 的结构图:

中介者模式的结构图

4. 代码的实现

根据上述结构图,很容易完成 中介者模式 的实现。

首先定义顶层组件 Component 和顶层中介者 Mediator

/**
 * @author mofan
 * @date 2024/2/16 22:05
 */
public abstract class Component {
    protected Mediator mediator;

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    public abstract void receive();

    public abstract void send();
}
/**
 * @author mofan
 * @date 2024/2/16 22:03
 */
public interface Mediator {
    /**
     * 注册
     */
    void register(Component component);

    /**
     * 转发
     */
    void relay(Component component);
}

然后实现 Component,以 ConcreteComponent1ConcreteComponent2 为例:

/**
 * @author mofan
 * @date 2024/2/16 22:13
 */
public class ConcreteComponent1 extends Component {
    @Override
    public void receive() {
        System.out.println("1 号组件收到请求");
    }

    @Override
    public void send() {
        System.out.println("1 号组件发出请求,请求转发...");
        mediator.relay(this);
    }
}
/**
 * @author mofan
 * @date 2024/2/16 22:13
 */
public class ConcreteComponent2 extends Component {
    @Override
    public void receive() {
        System.out.println("2 号组件收到请求");
    }

    @Override
    public void send() {
        System.out.println("2 号组件发出请求,请求转发...");
        mediator.relay(this);
    }
}

最后实现 Mediator,定义组件之间的交互与依赖:

/**
 * @author mofan
 * @date 2024/2/16 22:09
 */
public class ConcreteMediator implements Mediator {

    private final List<Component> list = new ArrayList<>();

    @Override
    public void register(Component component) {
        if (!list.contains(component)) {
            list.add(component);
            component.setMediator(this);
        }
    }

    @Override
    public void relay(Component component) {
        list.stream()
                .filter(i -> !i.equals(component))
                .forEach(Component::receive);
    }
}

在中介者中注册各个具体的组件,当某个组件发出请求时,其他组件会收到请求:

/**
 * @author mofan
 * @date 2024/2/16 22:24
 */
public class MediatorTest {
    @Test
    public void test() {
        Mediator mediator = new ConcreteMediator();
        ConcreteComponent1 component1 = new ConcreteComponent1();
        mediator.register(component1);
        ConcreteComponent2 component2 = new ConcreteComponent2();
        mediator.register(component2);

        // 组件 1 号发出请求,其他组件会收到请求
        component1.send();
    }
}
1 号组件发出请求,请求转发...
2 号组件收到请求

这个例子似乎与 观察者模式 很类似,它俩也的确很类似,但它们各自的目的是有区别的:

当中介者对象担任发布者的角色,而其他组件担任订阅者时,此时的 中介者模式 * 与 * 观察者模式 * 很类似,但 * 中介者模式 的实现并不仅此一种。

5. 模式的拓展

实际开发过程中可以采用以下两种方式对 中介者模式 进行简化:

  1. 不定义顶层中介者接口,将具体中介者对象定义为单例对象;
  2. 具体的组件不再持有中介者对象,而是在需要时直接获取。

此时 中介者模式 的结构图如下:

简化的中介者模式结构图

这种 中介者模式 * 的实现就难以看到 * 观察者模式 的影子了。

*个人愚见,只要是将类与类之间的依赖关系抽取到一个新类中,都能认为是使用了 * 中介者模式 ,至于什么样的依赖关系、新类的内部实现又是怎样的,这些都不重要。

首先定义单例的中介者 SimpleMediator 和组件顶层接口 SimpleComponent

/**
 * 简化的中介者,不定义接口,直接使用单例对象
 *
 * @author mofan
 * @date 2024/2/16 22:51
 */
public class SimpleMediator {
    private static final SimpleMediator MEDIATOR = new SimpleMediator();
    private final List<SimpleComponent> components;

    private SimpleMediator() {
        this.components = new ArrayList<>();
    }

    public static SimpleMediator getMediator() {
        return MEDIATOR;
    }

    public void register(SimpleComponent component) {
        if (!components.contains(component)) {
            components.add(component);
        }
    }

    public void relay(SimpleComponent component) {
        this.components.stream()
                .filter(i -> !i.equals(component))
                .forEach(SimpleComponent::receive);
    }
}
/**
 * @author mofan
 * @date 2024/2/16 22:53
 */
public abstract class SimpleComponent {

    public SimpleComponent() {
        SimpleMediator.getMediator().register(this);
    }

    /**
     * 接收
     */
    public abstract void receive();

    /**
     * 发送
     */
    public abstract void send();
}

然后定义具体的组件 SimpleConcreteComponent1SimpleConcreteComponent2

/**
 * @author mofan
 * @date 2024/2/16 23:00
 */
public class SimpleConcreteComponent1 extends SimpleComponent {

    @Override
    public void receive() {
        System.out.println("1 号简单组件收到请求");
    }

    @Override
    public void send() {
        System.out.println("1 号简单组件发出请求,请求转发...");
        SimpleMediator.getMediator().relay(this);
    }
}
/**
 * @author mofan
 * @date 2024/2/16 23:05
 */
public class SimpleConcreteComponent2 extends SimpleComponent {
    @Override
    public void receive() {
        System.out.println("2 号简单组件收到请求");
    }

    @Override
    public void send() {
        System.out.println("2 号简单组件发出请求,请求转发...");
        SimpleMediator.getMediator().relay(this);
    }
}

每实例化一个组件,就会把该组件注册到单例的中介者对象中,无需显式注册。

最终实现的逻辑和先前并无差别,都是当某个组件发出请求时,其他组件就会收到请求:

@Test
public void testSimpleMediator() {
    SimpleComponent component1 = new SimpleConcreteComponent1();
    // 实例化组件时,就会注册组件到中介者中
    new SimpleConcreteComponent2();

    // 组件 1 号发送请求,其他组件就会收到请求
    component1.send();
}
1 号简单组件发出请求,请求转发...
2 号简单组件收到请求
如果这篇文章对你有帮助,可以通过
支付宝
支付宝
微信
微信
请我喝杯 Coffee ☕


上一篇
Combinator Pattern
下一篇
再见癸卯,你好甲辰