设计模式
中介者模式
在现实生活中,对象与对象之间常常存在着复杂的交互关系,如:公司中员工与员工之间的关系;世界上国家与国家之间的关系;以及草原中动物与动物之间的关系。这种关系一般呈 “网状结构”,每个对象都与多个对象交互,不利于管理,且一旦有修改就会牵连到多个对象,非常复杂。
如果我们在这个 “网状结构” 中引入一个管理对象之间的关系的 “中介者”,将 “网状结构” 改为 “星型结构”,将大大降低各个对象之间的耦合度,降低系统的复杂度且方便扩展。如公司的微信群/钉钉群,国际关系中的联合国等等;以下以国际关系为例介绍 “网状结构” 与 “星型结构”。
网状结构:
星型结构
通过上面两幅图对比,我们发现 “星型结构” 比 “网状结构” 更加简单明了,更容易理解,且对象之间的耦合度更低,管理起来也更加方便。上面这种引入联合国作为 “中介者” 的思想就是中介者模式。
模式的定义
中介者模式(Mediator)也叫调停者模式,它定义一个中介对象来封装一系列的对象交互,中介者使得各对象不需要显式的互相引用,而使其耦合松散,而且可以独立的改变他们之间的交互;它是一种对象行为模式。
模式的优点如下:
- 类之间各司其职,符合迪米特法则。
- 降低了对象之间的耦合性,使得对象易于独立地被复用。
- 将对象间的一对多关联转变为一对一的关联,使对象间的关系易于理解和维护,提高系统的灵活性,使得系统易于维护和扩展。
- 可以将对象的行为和协作进行抽象,能够比较灵活的处理对象间的相互作用。
缺点:
- 将对象间的依赖关系封装到了中介者中,当对象越来越多时,中介者也会越来越臃肿,会变得复杂且难以维护,一旦中介者出现问题或不可用,则会影响到所有对象之间的交互。
模式的结构
关键概念:
- 同事类:如果一个对象会影响其他的对象,同时也会被其他对象影响,那么这两个对象称为同事类。
中介者模式的主要角色如下:
- 抽象中介者角色(Mediator):定义了同事对象到中介者对象的接口,用于各个同事类之间的通信。一般包括一个或几个抽象的事件方法,并由子类去实现。
- 具体中介者角色(Concrete Mediator):实现抽象中介者的方法,它需要知道所有具体同事类,并从具体同事接收消息,向具体同事对象发出命令。”
- 抽象同事类角色(Colleague):定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法。
- 具体同事类角色(Concrete Colleague):每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象,当需要与其他同事对象交互时,可以通过调度中介者对象来实现。
模式的 UML 类图如下:
模式的使用
下面以圆形属性计算为例,介绍中介者模式的使用。
在该实例中,圆共有三个属性类:半径、周长、面积;当其中任意一个属性类的值发生改变时,对应的其它两项属性都应发生改变;这时,这三个属性之间的交互关系就形成了网状结构。我们将这三个属性类之间的交互关系抽离出来形成一个用来管理其交互关系的中介者 “CircularCalculate”,单个属性类中只负责管理自身属性,对应改变其它属性的交互由中介者实现。
- 抽象中介者:
Mediator
定义了三个圆属性变化的方法;- 具体中介者:
CircularCalculate
包含了三个具体同事对象,分别为radius
、perimeter
、acreage
,同时实现了抽象中介者中三个圆属性变化的方法:
radiusChange
:当圆的半径发生改变时,同步改变圆的周长和面积。perimeterChange
:当圆的周长发生改变时,同步改变圆的半径和面积。acreageChange
:当圆的面积发生改变时,同步改变圆的半径和周长。- 抽象同事类:包含一个圆的属性值,和一个改变属性值的抽象方法。
- 具体同时类:实现了改变属性值的抽象方法,共有三个具体同事类,分别如下:
Radius
:半径类,提供了一个改变半径的方法,包含一个半径参数和一个具体中介者参数,当半径发生改变时,同步调用中介者中的radiusChange
方法同步改变perimeter
和acreage
。Perimeter
:周长类,提供了一个改变周长的方法,包含一个周长参数和一个具体中介者参数,当周长发生改变时,同步调用中介者中的perimeterChange
方法同步改变radius
和acreage
。Acreage
:面积类,提供了一个改变面积的方法,包含一个面积参数和一个具体中介者参数,当面积发生改变时,同步调用中介者中的acreageChange
方法同步改变radius
和perimeter
。
/**
* 抽象中介者
*/
public interface Mediator {
/**
* 半径改变
* @param radius
*/
void radiusChange(BigDecimal radius);
/**
* 周长改变
* @param perimeter
*/
void perimeterChange(BigDecimal perimeter);
/**
* 面积改变
* @param acreage
*/
void acreageChange(BigDecimal acreage);
}
/**
* 具体中介者 - 圆属性计算
*/
public class CircularCalculate implements Mediator {
final static BigInteger HUNDRED = BigInteger.valueOf(100);
private BigDecimal pi = new BigDecimal(3.14).setScale(2, BigDecimal.ROUND_DOWN);
private Radius radius;
private Perimeter perimeter;
private Acreage acreage;
public CircularCalculate(Radius radius, Perimeter perimeter, Acreage acreage) {
this.radius = radius;
this.perimeter = perimeter;
this.acreage = acreage;
}
@Override
public void radiusChange(BigDecimal radius) {
BigDecimal c = radius.multiply(new BigDecimal(2))
.multiply(pi)
.setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal area = radius.multiply(radius)
.multiply(pi)
.setScale(2, BigDecimal.ROUND_DOWN);
perimeter.setNumber(c);
acreage.setNumber(area);
}
@Override
public void perimeterChange(BigDecimal perimeter) {
BigDecimal r = perimeter.divide(new BigDecimal(2), 2, BigDecimal.ROUND_DOWN)
.divide(pi, 2, BigDecimal.ROUND_DOWN)
.setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal area = perimeter.divide(new BigDecimal(2), 2, BigDecimal.ROUND_DOWN)
.multiply(r)
.setScale(2, BigDecimal.ROUND_DOWN);
radius.setNumber(r);
acreage.setNumber(area);
}
@Override
public void acreageChange(BigDecimal acreage) {
BigDecimal square = acreage.divide(pi, 2, BigDecimal.ROUND_DOWN);
BigDecimal r = sqrt(square, 2, BigDecimal.ROUND_DOWN);
BigDecimal c = acreage.divide(r, 2, BigDecimal.ROUND_DOWN)
.multiply(new BigDecimal(2))
.setScale(2, BigDecimal.ROUND_DOWN);
radius.setNumber(r);
perimeter.setNumber(c);
}
/**
* 开平方根
*
* @param number
* @param scale
* @param roundingMode
* @return
*/
public BigDecimal sqrt(BigDecimal number, int scale, int roundingMode) {
if (number.compareTo(BigDecimal.ZERO) < 0)
throw new ArithmeticException("sqrt with negative");
BigInteger integer = number.toBigInteger();
StringBuffer sb = new StringBuffer();
String strInt = integer.toString();
int lenInt = strInt.length();
if (lenInt % 2 != 0) {
strInt = '0' + strInt;
lenInt++;
}
BigInteger res = BigInteger.ZERO;
BigInteger rem = BigInteger.ZERO;
for (int i = 0; i < lenInt / 2; i++) {
res = res.multiply(BigInteger.TEN);
rem = rem.multiply(HUNDRED);
BigInteger temp = new BigInteger(strInt.substring(i * 2, i * 2 + 2));
rem = rem.add(temp);
BigInteger j = BigInteger.TEN;
while (j.compareTo(BigInteger.ZERO) > 0) {
j = j.subtract(BigInteger.ONE);
if (((res.add(j)).multiply(j)).compareTo(rem) <= 0) {
break;
}
}
res = res.add(j);
rem = rem.subtract(res.multiply(j));
res = res.add(j);
sb.append(j);
}
sb.append('.');
BigDecimal fraction = number.subtract(number.setScale(0, BigDecimal.ROUND_DOWN));
int fracLen = (fraction.scale() + 1) / 2;
fraction = fraction.movePointRight(fracLen * 2);
String strFrac = fraction.toPlainString();
for (int i = 0; i <= scale; i++) {
res = res.multiply(BigInteger.TEN);
rem = rem.multiply(HUNDRED);
if (i < fracLen) {
BigInteger temp = new BigInteger(strFrac.substring(i * 2, i * 2 + 2));
rem = rem.add(temp);
}
BigInteger j = BigInteger.TEN;
while (j.compareTo(BigInteger.ZERO) > 0) {
j = j.subtract(BigInteger.ONE);
if (((res.add(j)).multiply(j)).compareTo(rem) <= 0) {
break;
}
}
res = res.add(j);
rem = rem.subtract(res.multiply(j));
res = res.add(j);
sb.append(j);
}
return new BigDecimal(sb.toString()).setScale(scale, roundingMode);
}
}
/**
* 抽象同事类
*/
public abstract class Colleague {
/**
* 具体值
*/
protected BigDecimal number;
public Colleague(BigDecimal number) {
this.number = number;
}
public BigDecimal getNumber() {
return number;
}
public void setNumber(BigDecimal number) {
this.number = number;
}
/**
* 值改变
* @param number
* @param graphics
*/
public abstract void changNumber(BigDecimal number, Mediator graphics);
}
/**
* 具体同事类 - 半径
*/
public class Radius extends Colleague{
public Radius(BigDecimal number) {
super(number);
}
@Override
public void changNumber(BigDecimal number, Mediator graphics) {
this.number = number;
graphics.radiusChange(number);
}
}
/**
* 具体同事类 - 周长
*/
public class Perimeter extends Colleague{
public Perimeter(BigDecimal number) {
super(number);
}
@Override
public void changNumber(BigDecimal number, Mediator graphics) {
this.number = number;
graphics.perimeterChange(number);
}
}
/**
* 具体同事类 - 面积
*/
public class Acreage extends Colleague{
public Acreage(BigDecimal number) {
super(number);
}
@Override
public void changNumber(BigDecimal number, Mediator graphics) {
this.number = number;
graphics.acreageChange(number);
}
}
/**
* 中介者模式测试类
*/
public class MediatorTest {
private static BigDecimal pi = new BigDecimal(3.14);
public static void main(String[] args) {
BigDecimal r = new BigDecimal(5);
BigDecimal c = r.multiply(new BigDecimal(2)).multiply(pi).setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal area = r.multiply(r).multiply(pi).setScale(2, BigDecimal.ROUND_DOWN);
Radius radius = new Radius(r);
Perimeter perimeter = new Perimeter(c);
Acreage acreage = new Acreage(area);
System.out.println("初始圆的数据=====>");
System.out.println("半径:" + radius.getNumber() + " 周长:" + perimeter.getNumber() + " 面积:" + acreage.getNumber());
System.out.println();
CircularCalculate cc = new CircularCalculate(radius, perimeter, acreage);
radius.changNumber(new BigDecimal(6), cc);
System.out.println("半径修改为 " + radius.getNumber() + " 后圆的数据=====>");
System.out.println("半径:" + radius.getNumber() + " 周长:" + perimeter.getNumber() + " 面积:" + acreage.getNumber());
System.out.println();
perimeter.changNumber(new BigDecimal(25.5), cc);
System.out.println("周长修改为 " + perimeter.getNumber() + " 后圆的数据=====>");
System.out.println("半径:" + radius.getNumber() + " 周长:" + perimeter.getNumber() + " 面积:" + acreage.getNumber());
System.out.println();
acreage.changNumber(new BigDecimal(500), cc);
System.out.println("面积修改为 " + acreage.getNumber() + " 后圆的数据=====>");
System.out.println("半径:" + radius.getNumber() + " 周长:" + perimeter.getNumber() + " 面积:" + acreage.getNumber());
System.out.println();
}
}
运行程序,结果如下:
模式的应用场景
中介者模式是一种比较常用的模式,也是一种比较容易被滥用的模式。适当的使用中介者模式可以使原本凌乱的对象关系变得清晰,但是如果滥用,则可能会带来反的效果。一般来说,只有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构,因此,大多数情况下,将对象间的依赖关系封装的同事类内部就可以的,没有必要非引入中介者模式。滥用中介者模式,只会让事情变的更复杂。
中介者模式的主要运用场景如下:
- 中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合。
- 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
- 定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。