在面向对象的程序开发世界中,万物皆对象,对应的行为也都会被封装到对应的对象中,而通常对象都不会是独立存在的,需要和其他对象相互通信,最直接的方式就是互相持有各自的对象,但是这种方式就一定会导致对象间耦合度十分地高, 在最坏的情况下, 每一个对象都需要知道其他所有对象,严重降低其可复用性和可扩展性。而且各对象之间需要相互协调才能完成工作任务,如果业务升级改动了其中一个对象,那么其他对象也可能需要跟着变动,如果系统复杂对象繁多通信复杂的话,整个系统结构将类似计算机网络中的网状结构十分的臃肿不堪,正是基于此,中介者模式应运而生。大量的连接关系使得一个对象不可能在没有其他对象的协助下工作(系统表现为一个不可分割的整体), 此时再对系统行为进行任何较大改动就十分困难. 因为行为被分布在许多对象中, 结果是不得不定义很多子类以定制系统的行为. 由此我们引入了中介者对象Mediator。附行为型设计模式系列文章列表:
中介者模式(Mediator Pattern)又称为调停者模式——用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互 (Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.)在中介者模式中,所有参与的对象只需要知道中介者对象Mediator,其他交互对象无须考虑,他们之间所有的交互都是通过中介者对象Mediator 间接完成的。整个模式的结构和计算机网络中的星型拓扑结构见下图(即任何两节点之间的通信都要通过中心节点进行转发,中心节点通常是集线器)类似,
在中介者模式中,Mediator就是“集线器”角色。所以通过中介者模式, 可以将原本的网状结构的系统优化成以中介者为中心的星型结构,每个具体对象之间不再与另一个对象直接交互, 而是通过中介者对象从中转发,因为中介者对象中存储了所有对象的关系。
中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。
降低了系统对象之间的耦合性,使得对象易于独立的被复用。
提高系统的灵活性,使得系统易于扩展和维护。
中介者模式的缺点就是中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。
当对象较多且对象之间交互需要相互依赖完成各自的工作时,为了避免牵一发而动全身,提高系统的后期的升级维护成本以及降低耦合度,可以考虑使用中介者模式是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行。简而言之,就是需要把形式网状结构的系统优化为星型拓扑结构的系统时可以使用。
中介者模式的实现起来也不算复杂,通用UML类图如下:
从UML类图中可以得知主要的其实核心角色就两类:中介者Mediator和协作者对象Colleague,其中为了扩展性通常都会定义一个接口或抽象类来作为中介者的父类(抽象只要定义出模式需要的角色,抽出大家的共性,当然前提是有共性,如果没有的话也没有必要一定抽出,因为这和依赖倒置原则是冲突的),协作者对象也是如此,接下来以ERP系统中常见的进销库存管理场景为例
采购部需要根据销售部和仓储部的反馈来决定是否采购以及采购的数量;而销售部也需要根据销售情况来督促仓储部备货和采购部及时补货;同时库房是有容积限制的,不可能无限大,所以当满仓的时候仓储部就会通知采购部门停止采购,同时督促销售部门进行销售尽快清仓。从以上分析来看,这三个部门都有各自的行为,但各自的行为都受到彼此多方的互相影响,也就是面向对象中的互相依赖关系,此时只有两三个部门还好,如果部门多了的话甚至跨公司的话,依赖关系将十分繁杂,如果采用传统模式设计ERP系统的话,那么后期维护成本将是十分巨大的,而中介者模式正是解决这类问题的良好选择之一,废话有点多,接下来正式开始实现:
package mediator;
public abstract class AbstractColleague {
protected AbstractMediator mediator;//每一个协作者都应该知道中介者
public AbstractColleague(AbstractMediator mediator) {
this.mediator = mediator;//每一个协作者都需要与中介者交互,所以在构造方法里传入中介者引用
}
}
/**
*
* @author Crazy.Mo
*仓储部需要做的工作有:入库、出库、库存数量、库存情况反馈到销售部和采购部
*/
public class Stock extends AbstractColleague {
public Stock(AbstractMediator mediator) {
super(mediator);
}
// 假设刚开始有100台电脑
private static int COMPUTER_NUMBER = 100;
// 入库
public void increase(int number) {
COMPUTER_NUMBER = COMPUTER_NUMBER + number;
System.out.println("库存数量为:" + COMPUTER_NUMBER);
}
// 出库
public void decrease(int number) {
COMPUTER_NUMBER = COMPUTER_NUMBER - number;
System.out.println("库存数量为:" + COMPUTER_NUMBER);
}
// 获得库存数量
public int getStockNumber() {
return COMPUTER_NUMBER;
}
//存货压力大了,就要通知采购人员不要采购,销售人员要尽快销售
public void clearStock() {
System.out.println("清理存货数量为:" + COMPUTER_NUMBER);
super.mediator.execute("stock.clear");//只是把消息告知给中介者,并没有直接和销售、采购部联系
}
}
import java.util.Random;
/**
*
* @author Crazy.Mo
* 销售部的工作有:正常销售、折价销售,反馈销售情况,其中前两者的工作受库存和采购的制约,所以通过中介者来间接完成
*/
public class Sale extends AbstractColleague {
public Sale(AbstractMediator mediator) {
super(mediator);
}
// 销售IBM电脑
public void sellIBMComputer(int number) {
super.mediator.execute("sale.sell", number);
System.out.println("销售IBM电脑" + number + "台");
}
// 反馈销售情况,0~100变化,0代表根本就没人买,100代表非常畅销,出一个卖一个
public int getSaleStatus() {
Random rand = new Random(System.currentTimeMillis());
int saleStatus = rand.nextInt(100);
System.out.println("IBM电脑的销售情况为:" + saleStatus);
return saleStatus;
}
// 折价处理
public void offSale() {
super.mediator.execute("sale.offsell");
}
}
/**
*
* @author Crazy.Mo
*采购部主要工作有:开始采购、停止采购
*/
public class Purchase extends AbstractColleague {
public Purchase(AbstractMediator mediator) {
super(mediator);
}
// 采购IBM电脑
public void buyIBMcomputer(int number) {
super.mediator.execute("purchase.buy", number);
}
// 不再采购IBM电脑
public void refuseBuyIBM() {
System.out.println("不再采购IBM电脑");
}
}
中介者对象就是负责转发各协作者对象的消息,所以中介者的实现很简单,首先他得知道有哪些协作者对象,最简单的方式就是持有所有协作者对象的引用,
public abstract class AbstractMediator {
protected Purchase purchase;
protected Sale sale;
protected Stock stock;
// 构造函数
public AbstractMediator() {
purchase = new Purchase(this);
sale = new Sale(this);
stock = new Stock(this);
}
// 中介者最重要的方法叫做事件方法,处理多个对象之间的关系
public abstract void execute(String str,Object...objects);
}
中介者Mediator定义了多个private方法,其目的是处理各个对象之间的依赖关系,就是说把原有一个对象要依赖多个对象的情况移到中介者的private方法中实现。在实际项目中,一般的做法是中介者按照职责进行划分,每个中介者处理一个或多个类似的关联请求。
package mediator;
public class Mediator extends AbstractMediator {
// 中介者最重要的方法,事件方法
public void execute(String str, Object... objects) {
if (str.equals("purchase.buy")) { // 当中介者接到采购人员的采购请求时
this.buyComputer((Integer) objects[0]);
} else if (str.equals("sale.sell")) { // 当中介者接到采购人员的采购请求时销售人员销售请求时
this.sellComputer((Integer) objects[0]);
} else if (str.equals("sale.offsell")) { // 当中介者接到采购人员的采购请求时销售人员时折价销售请求时
this.offSell();
} else if (str.equals("stock.clear")) { // 当中介者接到库存人员清仓处理请求时
this.clearStock();
}
}
// 采购电脑
private void buyComputer(int number) {
int saleStatus = super.sale.getSaleStatus();
if (saleStatus > 80) { // 销售情况良好
System.out.println("采购IBM电脑:" + number + "台");
super.stock.increase(number);
} else { // 销售情况不好
int buyNumber = number / 2; // 折半采购
System.out.println("采购IBM电脑:" + buyNumber + "台");
}
}
// 销售电脑
private void sellComputer(int number) {
if (super.stock.getStockNumber() < number) { // 库存数量不够销售
super.purchase.buyIBMcomputer(number);//通知采购部采购
}
super.stock.decrease(number);//库存直接减少
}
// 折价销售电脑
private void offSell() {
System.out.println("折价销售IBM电脑" + stock.getStockNumber() + "台");
}
// 清仓处理
private void clearStock() {
// 要求清仓销售
super.sale.offSale();
// 要求采购人员不要采购
super.purchase.refuseBuyIBM();
}
}
测试
public class MainMediatorClient {
public static void main(String[] args) {
AbstractMediator mediator = new Mediator();
// 采购人员采购电脑
System.out.println("------采购人员采购电脑--------");
Purchase purchase = new Purchase(mediator);
purchase.buyIBMcomputer(100);//采购人员想要采购电脑,首先会把这个信息传递到中介者,根据中介者的实现:首先根据销售部门反馈的状况良好则通知库房直接批准,情况不妙则折半采购
// 销售人员销售电脑
System.out.println("\n------销售人员销售电脑--------");
Sale sale = new Sale(mediator);
sale.sellIBMComputer(1);//销售人员想要卖电脑,首先会把这个信息传递到中介者,根据中介者的实现:首先根据库房状况,不够的话要求采购部采购,库存足够的话库房直接出库
// 库房管理人员管理库存
System.out.println("\n------库房管理人员清库处理--------");
Stock stock = new Stock(mediator);
stock.clearStock();//库房人员想要清库存,首先会把这个信息传递到中介者,根据中介者的实现:督促销售着才去折价销售采购部停止采购
}
}
在场景类中增加了一个中介者,然后分别传递到三个同事类中,三个类都具有相同的特性:只负责处理自己的活动(行为),与自己无关的活动就丢给中介者处理,程序运行的结果是相同的。从项目设计上来看,加入了中介者,设计结构清晰了很多,而且类间的耦合性大大减少,代码质量也有了很大的提升。
中介者模式很容易在系统中应用, 也很容易在系统中误用. 当系统出现了“多对多”交互复杂的对象群时, 不要急于使用中介者, 最好首先先反思系统的设计是否是合理. 由于中介者角色控制了集中化, 于是就把交互复杂性变成了中介者的复杂性, 使得中介者对象变得比任一个协作者对象都复杂. 在下列情况下建议使用中介者模式。