如果您有租房的经历,那么您对中介并不陌生。租房时,我们先去房屋租赁中介登记,告诉它您的租房需求,然后中介会按照您的要求为您筛选适合的房子。房子确定后,您会与中介签订租赁合同,缴纳费用,然后拿到钥匙……整个过程中,所有事项都由中介一手包办,入住以后房屋有任何问题,您都直接去找中介,您甚至可能并不知道房东是谁。这就是我们今天要说的模式——中介者模式。

mediator view

中介者模式来源于生活中的各个中介机构,不如前边提到的房产中介、贷款中介、票务中介等等。

1. 概念

DP 对中介者模式的定义是这样的:用一个 中介对象 来封装一系列的对象交互,各个对象 不显式地相互引用,从而使其 松散耦合,而且可以独立的改变它们之间的交互。这完全符合 迪米特法则

简而言之,中介者模式将多个相互引用的对象解耦,使得它们不直接交互,而是"有事找中介"。

除了生活中的中介机构外,中介者模式在软件中应用也很多,比如,服务网关,各个服务不直接通信,而是都通过网关来互相调用;又比如,MVC 模式中,service 层就是使用典型的中介者模式,因为 service 层依赖了多个 dao 层的类,而 dao 层的类之间并不直接交互……

2. 结构

中介者模式的类图如下:

mediator class

存在4个角色:

  1. Mediator: 抽象中介者,定义了通用接口让同事类之间进行通信

  2. ConcreteMediator: 具体中介者,实现 Mediator 的具体业务来支持同事类之间的通信

  3. Colleague: 抽象同事类,定义同事类通用接口,内部聚合 Mediator ,通过它与其他同事类交互

  4. ConcreteColleague: 具体同事类,调用 Mediator 实现与其他具体同事类的通信

在中介者模式中,同事类之间互不认识,但是他们都认识中介者,就好比租房时您不认识房东,但是房东和您都认识中介,通过中介来完成通信,因此,中介者是作为一个中心化的角色存在。

优点:

  1. 同事类之间完全结构,符合 迪米特法则]

  2. 同事类可以扩展和复用,并且减少了它们的子类数量

  3. 简化了对象模型,由多对多关系变成了一对多关系

  4. 中介者决定同事类之间如何交互,而抽象中介者对这种交互进行了抽象,便于扩展

缺点:

  1. 同事类之间的交互完全由中介者来控制,中介者复杂度增加

  2. 中心化的中介者出现问题,则同事类之间无法交互,即存在单点问题

3. 适用场景

中介者模式适用于以下场景:

  1. 多个对象间存在复杂的依赖关系,结构混乱且难以理解

  2. 一个对象引用其他多个对象,并直接与它们通信,导致难以复用该对象

  3. 一个对象被多个对象引用,难以扩展,又不想为其生成子类

4. 与其他模式的区别

  • 与观察者模式

上一篇 讲了观察者模式,与中介者模式非常类似。

  1. 结构类似,都定义了类似的4个角色,两个抽象角色和两个具体角色

  2. 两者实现的功能不同,观察者模式适用于一个对象改变而其他对象也需要做出改变的场景,而中介者模式重点在于解耦对象间多对多的复杂通信关系

  3. 中介者模式可以与观察者模式组合使用,如:中介者也作为通知发送者

    • 与外观模式

中介者模式与 外观模式 也类似:

  1. 都尝试在多个紧密耦合的类之间组织协作关系

  2. 外观模式是一种结构性模式,而中介者模式是一种行为型模式

  3. 外观模式只是简单的将各个子系统的接口进行组合再提供新的接口,本身并不实现额外的功能,子系统间可以进行直接通信;而中介者组合同事类,提供额外的功能以完成同事类间的通信,同事类之间不直接通信

5. 示例代码

来看看中介者模式的示例代码:

1、定义抽象同事类:

public interface Colleague {
    void sendMessage(String msg); (1)
    void receiveMessage(String msg); (2)
    void setMediator(Mediator mediator); (3)
    Mediator getMediator(); (4)
}
1向其他同事发送消息
2接收其他同事发来的消息
3设置中介者对象
4获取中介者对象

2、定义中介者

public interface Mediator {
    void sendMessage(Colleague colleague, String msg); (1)
}
1转发某个同事类发来的消息到另一个同事类

3、定义具体同事类

具体同事类1
public class ConcreteColleague1 implements Colleague {
    private Mediator mediator;
    public ConcreteColleague1(Mediator mediator) {
        this.mediator = mediator;
    }
    @Override
    public void sendMessage(String msg) {
        System.out.println(this.getClass().getSimpleName() + " 发送了消息: " + msg);
        this.mediator.sendMessage(this, msg); (1)
    }
    @Override
    public void receiveMessage(String msg) {
        System.out.println(this.getClass().getSimpleName() + " 收到了消息: " + msg);
    }
    @Override
    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }
    @Override
    public Mediator getMediator() {
        return this.mediator;
    }
}
1将消息发送的逻辑委托给中介者执行

具体同事类2的代码类似,就不贴出来了,有兴趣可以看文末源码。

两个具体同事类完成功能很简单:将发送消息的逻辑委托给中介者来执行,并打印谁发送的什么消息,还接收其他人发送来的消息。

4、具体中介者

public class ConcreteMediator implements Mediator {
    private ConcreteColleague1 colleague1; (1)
    private ConcreteColleague2 colleague2;
    @Override
    public void sendMessage(Colleague colleague, String msg) {
        if (colleague == colleague1) { (2)
            colleague2.receiveMessage(msg);
        } else if (colleague == colleague2) {
            colleague1.receiveMessage(msg);
        }
    }
    public void setColleague1(ConcreteColleague1 colleague1) {
        this.colleague1 = colleague1;
    }
    public void setColleague2(ConcreteColleague2 colleague2) {
        this.colleague2 = colleague2;
    }
}
1聚合两个同事类
2将消息发送给另一个同事类

5、客户端代码

public class MediatorClient {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
        ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
        ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
        mediator.setColleague1(colleague1);
        mediator.setColleague2(colleague2);

        colleague1.sendMessage("我是同事1");
        colleague2.sendMessage("我是同事2");
    }
}

执行后输出:

ConcreteColleague1 发送了消息: 我是同事1
ConcreteColleague2 收到了消息: 我是同事1
ConcreteColleague2 发送了消息: 我是同事2
ConcreteColleague1 收到了消息: 我是同事2

可以看到,整个交互过程,两个同事类没有直接交互,而都是通过中介者来完成的。

6. 总结

中介者模式将多对多的关系依赖关系解耦,并形成一对多的关系,完全遵循迪米特法则,中介者和同事类都可以独立扩展可复用,但是,中介者作为一个中心化的存在,功能太多,最后可以难以维护,或者导致单点问题。

另外,中介者模式、外观模式、观察者模式之间存在一定的相似性,使用时需要注意区分。

本文示例代码见: github


相关阅读