设计模式学习笔记(十七)中介者模式及其应用场景

中介者(Mediator)模式:定义了一个单独的中介对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。比如我们各种设备之间的通信,就是通过服务器作为中介对象来进行交互:

image-20220407094542645

一、中介者模式介绍

中介者又叫做调停模式,是一种对象行为型模式,它降低了对象之间的耦合性,让对象易于被独立地调用,是迪米特法则的典型应用,下面就来看看中介者模式的结构和实现:

1.1 中介者模式的结构

中介者模式主要通过引入用于协调其他对象或类之间相互调用的中介者类,为了让系统具有具有更好的灵活性和扩展性。其结构如下图所示:

image-20220407115009351

上面的类图中主要包含以下角色:

  • Mediator:抽象中介者,是中介者的接口/抽象类
  • ConcreteMeditor:中介者的具体实现,实现中介者接口,定义一个List来管理Colleague对象
  • Colleague:抽象同事类,定义同事类的接口/抽象类,保存中介者对象,实现同事类的公共方法
  • ConcreteColleague1、ConcreteColleague2:具体同事类,实现抽象同事类。通过中介者间接完成具体同事类之间的通信交互

1.2 中介者模式的实现

根据上面的类图,可以实现如下代码:

  1. 抽象中介者及其实现
/**
 * @description: 中介者抽象类
 * @author: wjw
 * @date: 2022/4/7
 */
public abstract class Mediator {

    /**注册同事类*/
    public abstract void register(Colleague colleague);

    /**处理接收逻辑*/
    public abstract void operation(Colleague colleague);
}

/**
 * @description: 具体中介者类
 * @author: wjw
 * @date: 2022/4/7
 */
public class ConcreteMediator extends Mediator{

    private List<Colleague> colleagues = new ArrayList<Colleague>();

    @Override
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMediator(this);
        }
    }

    @Override
    public void operation(Colleague colleague) {
        for (Colleague coll : colleagues) {
            if (!coll.equals(colleague)) {
                coll.receive();
            }
        }
    }
}
  1. 抽象同事类及其实现
/**
 * @description: 抽象同事类
 * @author: wjw
 * @date: 2022/4/7
 */
public abstract class Colleague {

    protected Mediator mediator;

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

    public abstract void receive();

    public abstract void send();
}

/**
 * @description: 具体同事类1
 * @author: wjw
 * @date: 2022/4/7
 */
public class ConcreteColleague1 extends Colleague{

    @Override
    public void receive() {
        System.out.println("具体同事类 ConcreteColleague1 接收请求");
    }

    @Override
    public void send() {
        System.out.println("具体同事类 ConcreteColleague1 发送请求");
        /*中介者进行转发*/
        mediator.operation(this);
    }
}

/**
 * @description: 具体同事类2
 * @author: wjw
 * @date: 2022/4/7
 */
public class ConcreteColleague2 extends Colleague{

    @Override
    public void receive() {
        System.out.println("具体同事类 ConcreteColleague2 接收到请求");
    }

    @Override
    public void send() {
        System.out.println("具体同事类 ConcreteColleague2 发送请求");
        mediator.operation(this);
    }
}
  1. 客户端测试类
/**
 * @description: 客户端
 * @author: wjw
 * @date: 2022/4/7
 */
public class Client {
    public static void main(String[] args) {
        Mediator concreteMediator = new ConcreteMediator();
        Colleague concreteColleague1 = new ConcreteColleague1();
        Colleague concreteColleague2 = new ConcreteColleague2();
        concreteMediator.register(concreteColleague1);
        concreteMediator.register(concreteColleague2);
        concreteColleague1.send();
        concreteColleague2.send();

    }
}

测试结果为:

具体同事类 ConcreteColleague1 发送请求
具体同事类 ConcreteColleague2 接收到请求
具体同事类 ConcreteColleague2 发送请求
具体同事类 ConcreteColleague1 接收请求

二、中介者模式应用场景

2.1 中介者模式的适用情况

如果遇到以下情况可以考虑使用中介者模式:

  1. 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解
  2. 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象
  3. 需要通过一个中间类来封装多个类中的行为,但又不想生成太多的子类

2.2 中介者模式在MVC模式中的应用

比如说,在MVC框架中,控制器(Controller)就是模型(Model)和视图(View)之间的中介者:

  • Model(模型):代表一个存取对象的数据,有Dao、Bean等等
  • View(视图):表示所看到的东西,比如网页、JSP等用于展示模型中的数据
  • Controller(控制器):作用于模型和视图中间,控制数据流向模型对象,在数据变化时更新视图

image-20220407153000058

三、中介者模式实战

3.1 ORM框架

我们知道在Java与数据库交互中JDBC可以完成对多种数据库操作,举一个利用JDBC查询的例子:

//1.加载MySQL驱动注入到DriverManager
Class.forName("com.mysql.cj.jdbc.Driver");
//2.提供JDBC连接的URL、用户名和密码
String url = "jdbc:mysql://localhost:3306/test_db?";
String username = "root";
String password = "root";
//3.创建数据库的连接
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建statement实例
Statement statement = connection.createStatement();
//5.1执行SQL语句,得到ResultSet对象,
String query = "select * from test";  //查询语句,也可以换成CRUD的其他语句
ResultSet resultSet = statement.executeQuery(query);
while(resultSet.next()){
    //5.2通过ResultSet读取数据后,将数据转换成JavaBean对象
} 
//6.关闭连接对象
connection.close();

在上面的步骤中,步骤1~4和6都可以封装重复执行,但是在第5步中,需要完成关系模型ResultSet到对象模型JavaBean的转换,而这一部分使用通用的方式封装这种复杂的转换是比较困难的,因此有ORM(Object Relational Mapping, 对象-关系映射)框架来解决对象转换关系模型的映射问题。同时也屏蔽了之前JDBC连接中的重复代码,只提供简单的API供开发人员进行使用。

image-20220407163737476

3.2 利用中介者模式模仿MyBatis核心功能

在本案例中我们通过模仿MyBatis 中核心ORM框架功能,来使用中介者模式。首先来看看ORM框架在数据库和应用交互中的位置:

image-20220407153504379

从图中可以看出,ORM框架位于数据库层和应用层中间,相当于两者之间的中介。在实际MyBatis 实现过程中,不仅用到了中介者模式,还有工厂模式和建造者模式。

在ORM框架实现的核心类中,包括加载配置文件、对XML进行解析、获取数据库session、操作数据库以及返回结果等步骤。在ORM内部的结构如下图所示(来自《重学Java设计模式》):

image-20220407165437935

  • 左上框内是对数据库的定义和处理,包括<T> T selectOne<T> List<T> selectList等等
  • 右上是对数据库配置的开启session的工厂处理类,工厂会操作DefaultSqlSession
  • 最后是核心类SqlSessionFactoryBuilder,它可以实现处理工厂、解析文件、拿session等操作

下面就来看看具体代码

实战代码

  1. 创建对应数据库、JavaBean和Dao接口

创建数据库design-mediatro,数据表userschool

image-20220407171710282

参考资料

《重学Java设计模式》

《Java设计模式》

《MyBatis技术内幕》

//c.biancheng.net/view/1393.html