】 中介者模式

1.定义

中介者模式(Mediator Pattern):用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互.

维基百科-中介者模式

2.实现

中介者模式

  • Mediator(抽象中介者):它定义一个接口,该接口用于与各同事对象之间进行通信
  • ConcreteMediator(具体中介者):它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,它维持了对各个同事对象的引用
  • Colleague(抽象同事类):它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信
  • ConcreteColleague(具体同事类):它是抽象同事类的子类;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中声明的抽象方法

客户端测试

/**
 * 测试客户端
 */
public class MediatorClient {

    public static void main(String[] args) {
        //定义中介者对象
        ConcreteMediator mediator = new ConcreteMediator();

        //定义同事对象
        ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
        ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);

        mediator.setColleagueA(colleagueA);
        mediator.setColleagueB(colleagueB);

        // operation
        colleagueA.changed();
    }
}

3.广义中介者

标准的中介者模式在实际使用中的困难

  1. 是否有必要为同事对象定义一个公共的父类?

java是单继承的,为了使用中介者模式,就让这些同事对象继承一个父类,这是很不好的;再者,这个父类目前也没有什么特别的公共功能,也就是说继承它也得不到多少好处.

在实际开发中,很多交互的对象本身是没有公共类的,强行加上一个父类,会让这些类实现起来很别扭

  1. 同事类有必要持有中介者对象吗?

同事类需要知道中介者对象,以便当他们发生改变的时候,能够通知中介者对象,但是否需要作为属性,并通过构造方法传入,这么强的依赖关系呢?

也可以有简单的方式去通知中介对象,比如把中介对象做成单例,直接在同事类方法里面去调用中介者对象.

  1. 是否需要中介者接口?

在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要维护的,因此中介者通常可以实现成单例.

  1. 中介者对象是否需要持有所有的同事?

虽说中介者对象需要知道所有的同事类,这样中介者才能与他们交互.但是是否需要作为属性这么强烈的依赖关系,而且中介者对象在不同的关系维护上,可能会需要不同的同事对象的实例,因此可以在中介者处理的方法里面去创建、或者获取、或者从参数传入需要的同事对象.

  1. 中介者对象只是提供一个公共的方法来接受同事对象的通知么?

在公共方法里,需要区分到底是谁调过来的,这还是简单的,还没有去区分到底是什么样的业务触发调用过来的,因为不同的业务,引起的与其它对象的交互是不一样的.

因此在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体的业务了.

对标准的中介者模式在实际使用中的改进 基于上面的考虑,在实际应用开发中,经常会简化中介者模式,来使开发变得简单,比如如下的简化:

  • 通常会去掉同事对象的父类,这样可以让任意的对象,只要需要相互交互,就可以成为同事;
  • 还有通常不定义Mediator接口,把具体的中介者对象实现成为单例
  • 另外一点就是同事对象不再持有中介者,而是在需要的时候直接获取中介者对象并调用;中介者也不再持有同事对象,而是在具体处理方法里面去创建,或者获取,或者从参数传入需要的同事对象.

把这样经过简化、变形使用的情况称为广义中介者.

4.广义中介者示例

/**
 * 部门类
 */
public class Dep {
    /**
     * 描述部门编号
     */
    private String depId;
    /**
     * 描述部门名称
     */
    private String depName;

    public String getDepId() {
        return depId;
    }

    public void setDepId(String depId) {
        this.depId = depId;
    }

    public String getDepName() {
        return depName;
    }

    public void setDepName(String depName) {
        this.depName = depName;
    }

    /**
     * 撤销部门
     * 
     * @return 是否撤销成功
     */
    public boolean deleteDep() {
        // 1:要先通过中介者去清除掉所有与这个部门相关的部门和人员的关系
        DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
        mediator.deleteDep(depId);
        // 2:然后才能真的清除掉这个部门
        // 请注意在实际开发中,这些业务功能可能会做到业务层去,
        // 而且实际开发中对于已经使用的业务数据通常是不会被删除的,
        // 而是会被做为历史数据保留
        return true;
    }
}
import java.util.ArrayList;
import java.util.Collection;

/**
 * 实现部门和人员交互的中介者实现类 说明:只示例实现撤销部门和人员离职的功能
 */
public class DepUserMediatorImpl {
    private static DepUserMediatorImpl mediator = new DepUserMediatorImpl();

    private DepUserMediatorImpl() {
        // 调用初始化测试数据的功能
        initTestData();
    }

    public static DepUserMediatorImpl getInstance() {
        return mediator;
    }

    /**
     * 记录部门和人员的关系
     */
    private Collection<DepUserModel> depUserCol = new ArrayList<DepUserModel>();

    /**
     * 初始化测试数据
     */
    private void initTestData() {
        // 准备一些测试数据
        DepUserModel du1 = new DepUserModel();
        du1.setDepUserId("du1");
        du1.setDepId("d1");
        du1.setUserId("u1");
        depUserCol.add(du1);

        DepUserModel du2 = new DepUserModel();
        du2.setDepUserId("du2");
        du2.setDepId("d1");
        du2.setUserId("u2");
        depUserCol.add(du2);

        DepUserModel du3 = new DepUserModel();
        du3.setDepUserId("du3");
        du3.setDepId("d2");
        du3.setUserId("u3");
        depUserCol.add(du3);

        DepUserModel du4 = new DepUserModel();
        du4.setDepUserId("du4");
        du4.setDepId("d2");
        du4.setUserId("u4");
        depUserCol.add(du4);

        DepUserModel du5 = new DepUserModel();
        du5.setDepUserId("du5");
        du5.setDepId("d2");
        du5.setUserId("u1");
        depUserCol.add(du5);
    }

    /**
     * 完成因撤销部门的操作所引起的与人员的交互,需要去除相应的关系
     *
     * @param depId
     *            被撤销的部门对象的编号
     * @return 是否已经正确的处理了因撤销部门所引起的与人员的交互
     */
    public boolean deleteDep(String depId) {
        // 请注意:为了演示简单,部门撤销后,原部门的人员怎么处理等后续业务处理,这里就不管了

        // 1:到记录部门和人员关系的集合里面,寻找跟这个部门相关的人员
        // 设置一个临时的集合,记录需要清除的关系对象
        Collection<DepUserModel> tempCol = new ArrayList<DepUserModel>();
        for (DepUserModel du : depUserCol) {
            if (du.getDepId().equals(depId)) {
                // 2:需要把这个相关的记录去掉,先记录下来
                tempCol.add(du);
            }
        }
        // 3:从关系集合里面清除掉这些关系
        depUserCol.removeAll(tempCol);

        return true;
    }

    /**
     * 完成因人员离职引起的与部门的交互
     *
     * @param userId
     *            离职的人员的编号
     * @return 是否正确处理了因人员离职引起的与部门的交互
     */
    public boolean deleteUser(String userId) {
        // 1:到记录部门和人员关系的集合里面,寻找跟这个人员相关的部门
        // 设置一个临时的集合,记录需要清除的关系对象
        Collection<DepUserModel> tempCol = new ArrayList<DepUserModel>();
        for (DepUserModel du : depUserCol) {
            if (du.getUserId().equals(userId)) {
                // 2:需要把这个相关的记录去掉,先记录下来
                tempCol.add(du);
            }
        }
        // 3:从关系集合里面清除掉这些关系
        depUserCol.removeAll(tempCol);

        return true;
    }

    /**
     * 在内部打印显示一下一个部门下的所有人员
     *
     * @param dep
     *            部门对象
     */
    public void showDepUsers(Dep dep) {
        for (DepUserModel du : depUserCol) {
            if (du.getDepId().equals(dep.getDepId())) {
                System.out.println("部门编号=" + dep.getDepId() + "下面拥有人员,其编号是:"
                        + du.getUserId());
            }
        }
    }

    /**
     * 在内部打印显示一下一个人员所属的部门
     *
     * @param user
     *            人员对象
     */
    public void showUserDeps(User user) {
        for (DepUserModel du : depUserCol) {
            if (du.getUserId().equals(user.getUserId())) {
                System.out.println("人员编号=" + user.getUserId() + "属于部门编号是:"
                        + du.getDepId());
            }
        }
    }

    /**
     * 完成因人员调换部门引起的与部门的交互
     *
     * @param userId
     *            被调换的人员的编号
     * @param oldDepId
     *            调换前的部门的编号
     * @param newDepId
     *            调换后的部门的编号
     * @return 是否正确处理了因人员调换部门引起的与部门的交互
     */
    public boolean changeDep(String userId, String oldDepId, String newDepId) {
        // 不实现了
        return false;
    }

    /**
     * 完成因部门合并操作所引起的与人员的交互
     *
     * @param colDepIds
     *            需要合并的部门的编号集合
     * @param newDep
     *            合并后新的部门对象
     * @return 是否正确处理了因部门合并操作所引起的与人员的交互
     */
    public boolean joinDep(Collection<String> colDepIds, Dep newDep) {
        // 不实现了
        return false;
    }
}
/**
 * 描述部门和人员关系的类
 */
public class DepUserModel {
    /**
     * 用于部门和人员关系的编号,用做主键
     */
    private String depUserId;
    /**
     * 部门的编号
     */
    private String depId;
    /**
     * 人员的编号
     */
    private String userId;

    public String getDepUserId() {
        return depUserId;
    }

    public void setDepUserId(String depUserId) {
        this.depUserId = depUserId;
    }

    public String getDepId() {
        return depId;
    }

    public void setDepId(String depId) {
        this.depId = depId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}
/**
 * 人员类
 */
public class User {
    /**
     * 人员编号
     */
    private String userId;
    /**
     * 人员名称
     */
    private String userName;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * 人员离职
     *
     * @return 是否处理成功
     */
    public boolean dimission() {
        // 1:要先通过中介者去清除掉所有与这个人员相关的部门和人员的关系
        DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
        mediator.deleteUser(userId);
        // 2:然后才能真的清除掉这个人员
        // 请注意,实际开发中,人员离职,是不会真的删除人员记录的,
        // 通常是把人员记录的状态或者是删除标记设置成已删除,
        // 只是不再参加新的业务,但是已经发生的业务记录是不会被清除掉的

        return true;
    }
}
public class Client {
    public static void main(String[] args) {
        DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
        // 准备要撤销的部门,仅仅需要一个部门编号
        Dep dep = new Dep();
        dep.setDepId("d1");
        Dep dep2 = new Dep();
        dep2.setDepId("d2");
        // 准备用于测试的人员,也只需要一个人员编号
        User user = new User();
        user.setUserId("u1");

        // 测试撤销部门,在运行之前,输出一下,看这个人员属于哪些部门
        System.out.println("撤销部门前------------------");
        mediator.showUserDeps(user);

        // 真正执行业务,撤销这个部门
        dep.deleteDep();

        // 再次输出一下,看这个人员属于哪些部门
        System.out.println("撤销部门后------------------");
        mediator.showUserDeps(user);

        // 测试人员离职,在运行之前,输出一下,看这个部门下都有哪些人员
        System.out.println("---------------------------------");
        System.out.println("人员离职前------------------");
        mediator.showDepUsers(dep2);

        // 真正执行业务,人员离职
        user.dimission();

        // 再次输出一下,看这个部门下都有哪些人员
        System.out.println("人员离职后------------------");
        mediator.showDepUsers(dep2);
    }
}

5.总结

优缺点

  • 松散耦合
  • 集中控制交互
  • 多对多变成一对多
  • 过度集中化

何时选用

  • 如果一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂
  • 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象.可以采用中介者模式,把这个对象跟其它对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了.

results matching ""

    No results matching ""