2、16中介者模式
定义
1)用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
2)中介者模式也叫做调停者模式。一个对象要和N个多个对象交流,就像对象间的战争,很混乱。这时,需要加入一个中心,所有的类都和中心交流,中心说怎么处理就怎么处理。
通用类图
1)Mediator抽象中介者角色
抽象中介者角色定义统一的接口,定义了同事对象到中介者对象的接口,用于各同事角色之间的通信。
2)ConcreteMediator具体中介者角色
具体中介者角色通过协调各同事角色实现协作行为,因为它必须依赖于各个同事角色。
具体中介者对象需要知道所有具体同事类,并从具体同事接收消息,向具体同事对象发出命令。
3)Colleague抽象同事角色
每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。
每个同事类的行为分为两种:
一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种行为叫做自发行为(self-method),与其他的同事类或中介者没有任何的依赖。
第二种是必须依赖中介者才能完成的行为,叫做依赖方法(dep-method)。
4)ConcreteColleague具体同事角色
具体同事类,每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象。
通用案例
/**
* 场景类
* TODO 可以发现下述案例中同事类使用构造函数注入中介者,而中介者使用getter/setter方法注入同事类.
* TODO 这是因为同事类必须有中介者,而中介者却可以只有部分同事类.
*/
public class Client1 {
public static void main(String[] args) {
}
}
/**
* 通用抽象的中介者
* TODO 可以发现同事类没有使用抽象类注入,那是因为同事类虽然有抽象,但是没有每个同事类必须要完成的业务方法.
* TODO 当然如果每个同事类都有相同的方法,比如execute()等,那么当然要注入抽象类,做到依赖倒置.
*/
abstract class Mediator{
//定义同事类
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
//通过get/set方法把同事类注入进来
public ConcreteColleague1 getC1() {
return c1;
}
public void setC1(ConcreteColleague1 c1) {
this.c1 = c1;
}
public ConcreteColleague2 getC2() {
return c2;
}
public void setC2(ConcreteColleague2 c2) {
this.c2 = c2;
}
//中介者模式的业务逻辑(相对比较复杂)
abstract void doSomething1();
abstract void doSomething2();
}
/**
* 通用中介者
* TODO 具体的中介者一般只有一个,即通用中介者
*/
class ConcreteMediator extends Mediator{
@Override
void doSomething1() {
//调用同事类的方法,只要是public方法都可以调用
super.c1.selfMethod1();
super.c2.selfMethod2();
}
@Override
void doSomething2() {
//调用同事类的方法,只要是public方法都可以调用
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}
/**
* 抽象同事类
*/
abstract class Colleague{
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
}
/**
* 具体同事类1
*/
class ConcreteColleague1 extends Colleague{
//通过构造函数传递中介者
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
//自有方法/自发行为
public void selfMethod1(){
//发现自己的业务逻辑
System.out.println("-----------selfMethod1()-----------");
}
//依赖方法
public void depMethod1(){
//处理自己的业务逻辑
//自己不能处理的业务逻辑,委托给中介者处理
super.mediator.doSomething1();
}
}
/**
* 具体同事类2
*/
class ConcreteColleague2 extends Colleague{
//通过构造函数传递中介者
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
//自有方法/自发行为
public void selfMethod2(){
//发现自己的业务逻辑
System.out.println("-----------selfMethod2()-----------");
}
//依赖方法
public void depMethod2(){
//处理自己的业务逻辑
//自己不能处理的业务逻辑,委托给中介者处理
super.mediator.doSomething2();
}
}
业务案例1:进销存管理
示例图
类图
代码
import java.util.Random;
/**
* 场景类
*/
public class Client2 {
public static void main(String[] args) {
//采购人员采购电脑
System.out.println("------------采购人员采购电脑-----------");
Purchase purchase = new Purchase();
purchase.buyIBMcomputer(100);
//销售人员销售电脑
System.out.println("\n------------销售人员销售电脑------------");
Sale sale = new Sale();
sale.sellIBMComputer(1);
//库存管理人员管理库存
System.out.println("\n------------官方管理人员清理库存------------");
Stock stock = new Stock();
stock.clearStock();
}
}
/**
* 采购管理
*/
class Purchase{
//采购IBM电脑
public void buyIBMcomputer(int number){
//访问库存
Stock stock = new Stock();
//访问销售
Sale sale = new Sale();
//电脑的销售情况
int saleStatus = sale.getSaleStatus();
//销售情况良好
if(saleStatus > 80){
System.out.println("销售情况良好:采购IBM电脑:" + number +"台.");
stock.increase(number);
}
//销售情况不好
else{
//销售情况不好
int buyNumber = number/2;
System.out.println("销售情况不好:采购IBM电脑:" + buyNumber +"台.");
}
}
//不再采购IBM电脑
public void refuseBuyIBM(){
System.out.println("不再采购IBM电脑");
}
}
/**
* 库存管理
*/
class Stock{
//刚开始有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(){
Purchase purchase = new Purchase();
Sale sale = new Sale();
System.out.println("清理存贷数量为:" + COMPUTER_NUMBER);
//要求折价销售
sale.offSale();
//要求采购人员不要采购
purchase.refuseBuyIBM();
}
}
/**
* 销售管理
*/
class Sale{
//销售IBM电脑
public void sellIBMComputer(int number){
//访问库存
Stock stock = new Stock();
//访问采购
Purchase purchase = new Purchase();
//库存数量不够销售
if(stock.getStockNumber() < number){
purchase.buyIBMcomputer(number);
}
System.out.println("销售IBM电脑:" + number + "台.");
stock.decrease(number);
}
//反馈销售情况,0-100之间变化,0代表根本就没人卖,100代表非常畅销,出一个卖一个
public int getSaleStatus(){
Random random = new Random(System.currentTimeMillis());
int saleStatus = random.nextInt(100);
System.out.println("IBM电脑的销售情况为:" + saleStatus);
return saleStatus;
}
//折价处理
public void offSale(){
//库房有多少卖多少
Stock stock = new Stock();
System.out.println("折价销售IBM电脑:" + stock.getStockNumber() + "台.");
}
}
业务案例2:进销存管理(扩展)
示例图
加入了一个中介者作为三个模块的交流核心,每个模块之间不再相互交流,要交流就通过中介者进行。每个模块只负责自己的业务逻辑,不属于自己的则丢给中介者来处理,简化了各模块之间的耦合关系,当然代码质量也有了很大的提升。
类图
代码
import java.util.Random;
/**
* 场景类
*/
public class Client3 {
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();
}
}
/**
* 抽象中介者
* TODO 实现中介者的抽象定义.定义抽象方法execute()
* TODO 可以根据业务的要求产生多个中介者,并划分各中介者的职责.
*/
abstract class AbstractMediator{
protected Purchase purchase;
protected Sale sale;
protected Stock stock;
//构造函数
public AbstractMediator() {
this.purchase = new Purchase(this);
this.sale = new Sale(this);
this.stock = new Stock(this);
}
//中介者最重要的方法叫做事件方法,处理多个对象之间的关系.
abstract void execute(String str,Object ... objects);
}
/**
* 具体中介者
* TODO 中介者Mediator定义了多个private方法,其目的是处理各个对象之间的依赖关系,就是说把原有一个对象要依赖多个对象的情况移到中介者的private方法中实现.
* TODO 在实际项目中,一般的做法是中介者按照职责进行划分,每个中介者处理一个或多个类似的关联请求.
*/
class Mediator extends AbstractMediator{
//中介者最重要的方法
@Override
void execute(String str, Object... objects) {
//采购电脑
if("purchase.buy".equals(str)){
this.buyComputer((Integer) objects[0]);
}
//销售电脑
else if("sale.sell".equals(str)){
this.sellComputer((Integer) objects[0]);
}
//折价销售
else if("sale.offsell".equals(str)){
this.offSell();
}
//清仓处理
else if("stock.clear".equals(str)){
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("折价销售电脑" + stock.getStockNumber() + "台.");
}
//清仓处理
private void clearStock(){
//要求清仓销售
super.sale.offSale();
//要求采购人员不要采购
super.purchase.refuseBuyIBM();
}
}
/**
* 抽象同事类
* TODO 各个具体同事类具有相同的特性:只负责处理自己的活动(行为),与自己无关的活动就丢给中介者处理,程序运行的结果是相同的.
*/
abstract class AbstractColleague{
protected AbstractMediator mediator;
public AbstractColleague(AbstractMediator mediator) {
this.mediator = mediator;
}
}
/**
* 修改后的采购管理
*/
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电脑");
}
}
/**
* 修改后的库存管理
*/
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");
}
}
/**
* 修改后的销售管理
*/
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 random = new Random(System.currentTimeMillis());
int saleStatus = random.nextInt(100);
System.out.println("IBM电脑的销售情况为" + saleStatus);
return saleStatus;
}
//折价处理
public void offSale(){
super.mediator.execute("sale.offsell");
}
}
优点
1)减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。
2)由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间交互上来,也就是站在一个更宏观的角度去看待系统。
缺点
中介者会膨胀的很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。
实际应用
1)生活上的例子
机场调度中心、中介服务、媒体网关MSN等。
2)MVC框架
比如Struts,MVC框架,其中的C(Controller)就是一个中介者,叫做前端控制器(Front Controller),它的作用就是把M(Model,业务逻辑)和V(View,视图)隔离开,协调M和V的工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少M和V的依赖关系。MVC框架已经成为一个非常流程且成熟的框架,这也是中介者模式的优点的一个体现。
使用场景
1)N个对象之间产生了相互的依赖关系(N>2).
2)多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能,在这种情况下一般建议采用中介者模式,降低变更引起的风险扩散.
3)产品开发.一个明显的例子就是MVC框架,把中介者模式应用到产品中,可以提升产品的性能和扩展性,但是对于项目开发就未必,因为项目是以交付投产为目标,而产品是以稳定、高效、扩展为宗旨。
4)中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了”多对多”就复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。
5)中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。
其他
1)总结的这个”设计模式之禅”中的中介者模式很少用到接口或抽象类,这与依赖倒置原则是冲突的,原因是,首先,既然是同事类而不是兄弟类(有相同的血脉),那就说明这些类之间是协作关系,完成不同的任务,处理不同的业务,所以不能在抽象类或接口中严格定义同事类必须具有的方法(从这点也可以看出继承是高侵入性的)。如果两个对象不能提炼出共性,那就不要刻意去追求两者的抽象,抽象只要定义出模式需要的角色即可。
2)抽象出一个具有共性的中介者不太可能,因为一个中介者抽象类一般只有一个实现者,除非中介者逻辑非常复杂,代码量非常大,这时才会出现多个中介者的情况,所以对于中介者来说,抽象已经没有太多的必要。
3)中介者模式是一个非常好的封装模式,也是一个很容易被滥用的模式。
4)尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低其可复用性了。因为大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以,对系统的行为进行任何较大的改动就十分困难了。
标题:设计模式(十六)行为模式(对象行为型模式)中介者模式-MEDIATOR
作者:yazong
地址:https://blog.llyweb.com/articles/2020/08/01/1596287758511.html