如果在一个系统中对象之间存在多对多的相互关系,可以将对象之间的一些交互行为从各个对象中分离出来,集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为相对简单的一对多关系。
中介者模式又称为调停者模式,它是一种对象行为型模式。在中介者模式中,通过引入中介者对象来简化对象之间的复杂交互,中介者模式是迪米特法则的一个典型应用。
1.中介者模式结构
在中介者模式中引入了用于协调其他对象/类之间相互调用的中介者类,为了让系统具有更好的灵活性和可扩展性,通常还提供了抽象中介者,如图1所示。
(1)Mediator(抽象中介者):它定义一个接口,该接口用于与各同事对象之间进行通信。
(2)ConcreteMediator(具体中介者):它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,他维持了对各个同事对象的引用
(3)Colleague(抽象同事类):它定义各个同事类公有的方法,并声明了一些抽象方法供子类实现,同时它维护了一个对抽象中介者类的引用,其子类可以通过该引用与中介者通信
(4)ConcreteColleague(具体同事类):它是抽象同事类的子类,每一个同事对象在需要和其他同事对象通信时先于中介者通信,通过中介者间接完成与其他同事类的通信;在具体同事类中实现了抽象类同事类中声明的抽象方法。
2.中介者模式实现
中介者模式的核心在于中介类的引入,在中介者模式中,中介者类承担了两个方面的职责:
(1)中转作用(结构性):通过中介者提供的中转作用,各个同事对象不再需要显示地引用其他同事,当需要和其他同事进行通信时可通过中介者实现间接调用。该中转作用属于中介者在结构上的支持
(2)协调作用(行为性):中介者可以更进一步地对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
在中介者模式中,典型的抽象中介者类代码如下:
package MediatorModel;
import java.util.ArrayList;
import java.util.List;
public abstract class Mediator {
//用于存储同事对象
protected List<Colleague> colleagueList=new ArrayList<>();
//注册方法,用于增加同事对象
public void register(Colleague colleague){
colleagueList.add(colleague);
}
//声明抽象的业务方法
public abstract void operation();
}
在抽象中介者中可以定义一个同事类的集合,用于存储同事对象并提供注册方法,同时声明了具体中介者所具有的方法。在具体中介者类中将实现这些抽象方法,典型的具体中介者类代码如下:
package MediatorModel;
public class ConcreteMediator extends Mediator{
//实现业务方法,封装同事之间的调用
@Override
public void operation() {
//通过中介者调用同事类的方法
((Colleague)colleagueList.get(0)).method1();
}
}
在具体中介者类中将调用同事类的方法,在调用时可以增加一些自己的业务代码对调用进行控制。
在抽象同事类中维持了一个抽象中介者的引用,用于调用中介者的方法。典型的抽象同事类代码如下:
package MediatorModel;
public abstract class Colleague {
//维持一个抽象中介者的引用
protected Mediator mediator;
public Colleague(Mediator mediator){
this.mediator=mediator;
}
//声明自身方法,处理自己的行为
public abstract void method1();
//定义依赖方法,与中介者进行通信,由于是公共的行为,所以放到父类
public void method2(){
mediator.operation();
}
}
在抽象同事类中声明了同事类的抽象方法,而在具体同事类中将实现这些方法。典型的具体同事类代码如下:
package MediatorModel;
public class ConcreteColleague extends Colleague{
public ConcreteColleague(Mediator mediator){
super(mediator);
}
//实现自身方法
@Override
public void method1() {
}
}
在具体同事类ConcreteColleague中实现了在抽象同事类中声明的方法,其中方法method1()是同事类的自身方法(Self-Method),用于处理自己的行为;而方法method2()是依赖方法(Depend-Method),用于调用在中介者中定义的方法,依赖中介者来完成相应的行为,例如调用另一个同事类的相关方法。
3.中介者模式应用实例
图3只是一个结构示意图,在具体实现时为了确保系统具有更好的灵活性和可扩展性,需要定义抽象中介者和抽象组件类,其中抽象组件类是所以具体组件类的公共父类,类图如图4所示(该类图使用IDEA自动生成,组合关系应为聚合关系)。
(1)Mediator:抽象中介者类。
package MediatorExample;
public abstract class Mediator {
public abstract void componentChanged(Component component);
}
(2)ConcreteMediator:具体中介者类。
package MediatorExample;
public class ConcreteMediator extends Mediator{
//维持对各个同事的引用
public ComponentBox cBox;
public ComponentList cList;
public ComponentText cText;
public ComponentButton cButton;
//封装各个同事对象之间的交互
@Override
public void componentChanged(Component component) {
if(component==cBox){
System.out.println("从组合框中选择用户");
cBox.select();
cText.setText();
}
else if(component==cList){
System.out.println("从列表中选择用户");
cBox.select();
cText.setText();
}
else if(component==cText){
System.out.println("请在文本框输入内容");
}
else if(component==cButton){
System.out.println("单击按钮增加");
cList.doSomething();
cBox.doSomething();
cText.doSomething();
}
}
}
(3)Component:抽象组件类,充当抽象同事类。
package MediatorExample;
import java.util.Optional;
public abstract class Component {
protected Mediator mediator;
public void setMediator(Mediator mediator){
this.mediator=mediator;
}
//转发调用
public void changed(){
mediator.componentChanged(this);
}
//被子类重写,虽然方法名相同,但子类的具体行为有差别
public abstract void doSomething();
}
(4)Button:按钮类,充当具体同事类。
package MediatorExample;
public class ComponentButton extends Component{
@Override
public void doSomething() {
System.out.println("按钮被点击了");
}
}
(5)List:列表框类,充当具体同事类。
package MediatorExample;
public class ComponentList extends Component{
@Override
public void doSomething() {
System.out.println("列表增加一项:张无忌");
}
//具体同事的独特行为
public void select(){
System.out.println("列表选中:小龙女");
}
}
(6)Box:组合框类,充当具体同事类。
package MediatorExample;
public class ComponentBox extends Component{
@Override
public void doSomething() {
System.out.println("组合框增加一项:张无忌");
}
//具体同事类的独特行为
public void select(){
System.out.println("组合框选中:小龙女");
}
}
(7)Text:文本框类,充当具体同事类。
package MediatorExample;
public class ComponentText extends Component{
@Override
public void doSomething() {
System.out.println("信息增加后文本框清零");
}
//具体同事类的独特行为
public void setText(){
System.out.println("文本框显示:小龙女");
}
}
(8)Client:客户端测试类。
package MediatorExample;
public class Client {
public static void main(String[] args){
ConcreteMediator mediator=new ConcreteMediator();
ComponentBox cBox=new ComponentBox();
ComponentButton cButton=new ComponentButton();
ComponentText cText=new ComponentText();
ComponentList cList=new ComponentList();
cBox.setMediator(mediator);
cButton.setMediator(mediator);
cText.setMediator(mediator);
cList.setMediator(mediator);
mediator.cBox=cBox;
mediator.cButton=cButton;
mediator.cText=cText;
mediator.cList=cList;
cBox.changed();
System.out.println("----------------------------------");
cButton.changed();
System.out.println("----------------------------------");
cText.changed();
System.out.println("----------------------------------");
cList.changed();
}
}
运行结果如下。
在引入中介者后,同事之间的复杂交互由中介者间接实现,当某个组件类changed()方法被调用时,中介者的component Changed()将被调用,在中介者的component Changed()方法中再逐个调用与该组件有交互的其他组件的相关方法。如果某个组件类需要与新的组件进行交互,无须修改已有组件类的源代码,只需修改中介者或者对现有中介者进行扩展即可,系统具有更好的灵活性和可扩展性。
更直观地看透中介者模式:我们从同事类入手,每个具体同事类都有自己的行为(不与其他同事交互),有的是公共行为可以提取到抽象同事类然后具体同事类重写,有的是独特行为每个具体同事类特有的;有的是交互行为用于与其他同事类交互,由于是通过中介者进行交互,需要调用中介者的方法,因此同事类需保持中介者的引用;来到中介者这边,中介者需要知道是哪个具体同事类要与其他同事类进行交互(所以某个具体同事类在调用中介者的方法时,需要把自己作为参数传进去),因此依赖具体同事类,并在交互时进行判断,调用其他具体同事类的方法。这样,同事类之间的交互便通过中介者完成了。
参考:Java设计模式(刘伟),图解设计模式