设计模式——中介者模式

本文介绍中介模式。

概念

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。

结构和工作原理

  • Mediator:抽象中介类,定义一个接口,使接口用于提供各个同事对象之间的通信
  • ConcreteMediator:具体中介类,抽象中介者的子类,通过协调各个同事对象来实现协作行为
  • Colleague:抽象同事类,定义各个同事的共有方法
  • ConcreteColleague:具体同事类,抽象同事类的子类,每个同事对象都引用一个中介者对象,通过中介者来完成与其他同事类的通信

当对象与对象之间存在大量当关联关系,对象之间相互引用,形成网状的高度耦合的依赖关系,会导致系统的结构非常复杂。使用中介者模式,将依赖全部转移到中介者,将之前对象之间的调用协作改造为对中介者的调用请求。通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构。

核心思想:
中介者承担了中转作用和协调作用。中介者类是中介者模式的核心,它对整个系统进行控制和协调,简化了对象之间的交互,还可以对对象间的交互进行进一步的控制。

  • 中转作用:中转体现在中介者持有同事类的结构性,各个同事类引用中介类,通过中介类和其他同事类进行通信。
  • 协调作用:中介者作为接受、发起同时类请求的中间者,需要了解各个同事之间调用的关系,增加协同同事类工作的逻辑。本质上来讲,是将原先同事之间调用的代码抽取放置在同一个类中,并将原先的单个方法调用改造成分布在两个同事类内的两个方法,同时可以增加额外的控制逻辑。

中介者模式的主要优点:

  • 简化了对象之间的交互
  • 将各同事解耦
  • 减少子类生成,对于复杂的对象之间的交互
  • 可以简化各同事类的设计和实现

中介者模式主要缺点:

  • 具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。

中介者模式适用情况包括:

  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解
  • 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

代码示例

@Getter
@Setter
public class RentContext {

private Integer roomSize;

private BigDecimal rent;

private Boolean hasRoom;

public RentContext(Integer roomSize, BigDecimal rent) {
this.roomSize = roomSize;
this.rent = rent;
}
}
public abstract class RentMediator {

protected Landlord landlord;

protected Tenant tenant;

protected BigDecimal mediatorMoney;

abstract void receiveMoney(BigDecimal money);

abstract void offerRoom(Boolean hasRoom);

abstract void requireRoom(Integer roomSize, BigDecimal rent);

public Landlord getLandlord() {
return landlord;
}

public void setLandlord(Landlord landlord) {
this.landlord = landlord;
}

public Tenant getTenant() {
return tenant;
}

public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
}
public abstract class RentUser {

abstract BigDecimal pay();
}
@Slf4j
public class ConcreteRentMediator extends RentMediator {

private static Gson gson = new Gson();

private RentContext rentContext;

public ConcreteRentMediator() {
}

@Override
void receiveMoney(BigDecimal money) {
if (this.mediatorMoney == null) {
this.mediatorMoney = money;
} else {
this.mediatorMoney = this.mediatorMoney.add(money);
}
}

@Override
public void offerRoom(Boolean hasRoom) {
this.rentContext.setHasRoom(hasRoom);
if (hasRoom) {
receiveMoney(landlord.pay());
} else {
tenant.withdrawMoney(this.mediatorMoney);
this.mediatorMoney = BigDecimal.ZERO;
}
log.info("ConcreteRentMediator- offerRoom- {}", hasRoom);
log.info("ConcreteRentMediator- offerRoom- done earn {} money", this.mediatorMoney);
}

@Override
void requireRoom(Integer roomSize, BigDecimal rent) {
this.rentContext = new RentContext(roomSize, rent);
receiveMoney(tenant.pay());
landlord.offerRoom(rentContext.getRoomSize(), rentContext.getRent());
}
}
@Slf4j
@Setter
public class Landlord extends RentUser {

private RentMediator rentMediator;

public Landlord(RentMediator rentMediator) {
this.rentMediator = rentMediator;
rentMediator.setLandlord(this);
}

@Override
BigDecimal pay() {
return BigDecimal.valueOf(500);
}

public void offerRoom(Integer roomSize, BigDecimal rent) {
BigDecimal divide = rent.divide(BigDecimal.valueOf(roomSize));
log.info("Landlord- offerRoom- divide:{}", divide);
if (divide.compareTo(BigDecimal.valueOf(50)) >= 0) {
rentMediator.offerRoom(true);
} else {
rentMediator.offerRoom(false);
}
}
}
@Getter
@Setter
@Slf4j
public class Tenant extends RentUser {

private RentMediator rentMediator;

private Integer roomSize;

private BigDecimal rent;

public Tenant(RentMediator rentMediator, Integer roomSize, BigDecimal rent) {
this.rentMediator = rentMediator;
this.roomSize = roomSize;
this.rent = rent;
rentMediator.setTenant(this);
}

@Override
BigDecimal pay() {
return BigDecimal.valueOf(500);
}

public void withdrawMoney(BigDecimal withdraw) {
log.info("Tenant- withdrawMoney- {}", withdraw);
}

public void requireRoom() {
log.info("Tenant- requireRoom- roomSize:{},rent:{}", roomSize, rent);
rentMediator.requireRoom(roomSize, rent);
}
}

public class MediatorClient {

public static void main(String[] args) {
RentMediator rentMediator = new ConcreteRentMediator();
Tenant tenant = new Tenant(rentMediator, 20, BigDecimal.valueOf(1000));
Landlord landlord = new Landlord(rentMediator);
tenant.requireRoom();

RentMediator rentMediator1 = new ConcreteRentMediator();
Tenant tenant1 = new Tenant(rentMediator1, 20, BigDecimal.valueOf(500));
Landlord landlord1 = new Landlord(rentMediator1);
tenant1.requireRoom();
}
}
15:12:49.091 [main] INFO com.nopainanymore.designpattern.behavior.mediator.Tenant - Tenant- requireRoom- roomSize:20,rent:1000
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.Landlord - Landlord- offerRoom- divide:50
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.ConcreteRentMediator - ConcreteRentMediator- offerRoom- true
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.ConcreteRentMediator - ConcreteRentMediator- offerRoom- done earn 1000 money
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.Tenant - Tenant- requireRoom- roomSize:20,rent:500
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.Landlord - Landlord- offerRoom- divide:25
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.Tenant - Tenant- withdrawMoney- 500
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.ConcreteRentMediator - ConcreteRentMediator- offerRoom- false
15:12:49.096 [main] INFO com.nopainanymore.designpattern.behavior.mediator.ConcreteRentMediator - ConcreteRentMediator- offerRoom- done earn 0 money

参考资料

https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/mediator.html
https://www.runoob.com/design-pattern/mediator-pattern.html

Author: nopainanymore
Link: http://nopainanymore.me/DesignPattern-Mediator/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
wechat