跳至主要內容

行为型中介模式

gavin-james设计模式设计模式大约 8 分钟

中介者模式基本介绍

中介者模式(Mediator Pattern)又叫 调停模式,它是迪米特法则的典型应用。用一个 中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其 耦合松散,而且可以独立地改变它们之间的交互。

中介者模式属于行为型模式,使代码易于维护。

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是「网状结构」,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他(她)所有朋友的电话;而且,朋友中如果有人的电话修改了,他(她)必须让其他所有的朋友一起修改,这叫作「牵一发而动全身」,非常复杂。

如果把这种「网状结构」改为「星形结构」的话,将大大降低它们之间的「耦合性」,这时只要找一个「中介者」就可以了。如前面所说的「每个人必须记住所有朋友电话」的问题,只要在网上建立一个每个朋友都可以访问的「通信录」就解决了。这样的例子还有很多,例如,你刚刚参加工作想租房,可以找「房屋中介」;或者,自己刚刚到一个陌生城市找工作,可以找「人才交流中心」帮忙。

比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用。还有大家常用的 QQ 聊天程序的「中介者」是 QQ 服务器。所有这些,都可以采用「中介者模式」来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。

主要优点

  • 类之间各司其职,符合迪米特法则
  • 降低了对象之间的耦合性,使得对象易于独立地被复用
  • 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展

主要缺点

中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。

中介者模式的结构与实现

中介者模式包含以下主要角色:

  • 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法
  • 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色
  • 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能
  • 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互

中介者模式的结构图如下所示:

image-20220326163809453
image-20220326163809453

代码实现:

// 抽象中介者
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); //转发
}
// 具体中介者
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<Colleague>();
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
// 抽象同事类
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}
// 具体同事类
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("具体同事类1收到请求。");
    }
    public void send() {
        System.out.println("具体同事类1发出请求。");
        mediator.relay(this); //请中介者转发
    }
}
// 具体同事类
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("具体同事类2收到请求。");
    }
    public void send() {
        System.out.println("具体同事类2发出请求。");
        mediator.relay(this); //请中介者转发
    }
}
public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
}

智能家具项目

智能家具包括各种设备:闹钟、咖啡机、电视机、窗帘等。

主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起 -> 咖啡机开始 -> 做咖啡 -> 窗帘自动落下 -> 电视机开始播放。

传统方案解决智能家具项目

image-20220326163154437
image-20220326163154437

当各电器对象有多种状态改变时,相互之间的调用关系会比较复杂。

各个电器对象彼此联系,你中有我,我中有你,不利于松耦合。

各个电器对象之间所传递的消息(参数),容易混乱。

当系统增加一个新的电器对象时,或者执行流程改变时,代码的可维护性、扩展性都不理想 考虑 中介者模式

中介者模式解决智能家庭项目

思路分析和类图:

image-20220326163930542
image-20220326163930542

代码实现:

中介者类

// 中介者抽象类
public abstract class Mediator {
	// 将给中介者对象,加入到集合中
	public abstract void Register(String colleagueName, Colleague colleague);

	// 接收消息, 具体的同事对象发出
	public abstract void GetMessage(int stateChange, String colleagueName);

	public abstract void SendMessage();
}
// 具体的中介者类
public class ConcreteMediator extends Mediator {
	// 集合,放入所有的同事对象
	private HashMap<String, Colleague> colleagueMap;
	private HashMap<String, String> interMap;

	public ConcreteMediator() {
		colleagueMap = new HashMap<String, Colleague>();
		interMap = new HashMap<String, String>();
	}

	@Override
	public void Register(String colleagueName, Colleague colleague) {
		colleagueMap.put(colleagueName, colleague);
		if (colleague instanceof Alarm) {
			interMap.put("Alarm", colleagueName);
		} else if (colleague instanceof CoffeeMachine) {
			interMap.put("CoffeeMachine", colleagueName);
		} else if (colleague instanceof TV) {
			interMap.put("TV", colleagueName);
		} else if (colleague instanceof Curtains) {
			interMap.put("Curtains", colleagueName);
		}
	}

	// 具体中介者的核心方法
	// 1. 根据得到消息,完成对应任务
	// 2. 中介者在这个方法,协调各个具体的同事对象,完成任务
	@Override
	public void GetMessage(int stateChange, String colleagueName) {
		//处理闹钟发出的消息
		if (colleagueMap.get(colleagueName) instanceof Alarm) {
			if (stateChange == 0) {
				((CoffeeMachine) (colleagueMap.get(interMap
						.get("CoffeeMachine")))).StartCoffee();
				((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
			} else if (stateChange == 1) {
				((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
			}
		} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
			((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
					.UpCurtains();
		} else if (colleagueMap.get(colleagueName) instanceof TV) {  // 如果 TV 发现消息

		} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
			// 如果是以窗帘发出的消息,这里处理 ...
		}
	}
	@Override
	public void SendMessage() {
	}
}

智能家具类

// 智能家具抽象类
public abstract class Colleague {
    private Mediator mediator;
    public String name;

    public Colleague(Mediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }

    public Mediator GetMediator() {
        return this.mediator;
    }

    public abstract void SendMessage(int stateChange);
}
// 咖啡机
public class CoffeeMachine extends Colleague {

    public CoffeeMachine(Mediator mediator, String name) {
        super(mediator, name);
        mediator.Register(name, this);
    }

    @Override
    public void SendMessage(int stateChange) {
        this.GetMediator().GetMessage(stateChange, this.name);
    }

    public void StartCoffee() {
        System.out.println("It's time to startcoffee!");
    }

    public void FinishCoffee() {
        System.out.println("After 5 minutes!");
        System.out.println("Coffee is ok!");
        SendMessage(0);
    }
}
// 电视机
public class TV extends Colleague {

	public TV(Mediator mediator, String name) {
		super(mediator, name);
		mediator.Register(name, this);
	}

	@Override
	public void SendMessage(int stateChange) {
		this.GetMediator().GetMessage(stateChange, this.name);
	}

	public void StartTv() {
		System.out.println("It's time to StartTv!");
	}

	public void StopTv() {
		System.out.println("StopTv!");
	}
}
// 窗帘
public class Curtains extends Colleague {

	public Curtains(Mediator mediator, String name) {
		super(mediator, name);
		mediator.Register(name, this);
	}

	@Override
	public void SendMessage(int stateChange) {
		this.GetMediator().GetMessage(stateChange, this.name);
	}

	public void UpCurtains() {
		System.out.println("I am holding Up Curtains!");
	}
}
// 闹钟
public class Alarm extends Colleague {
	// 构造器
	public Alarm(Mediator mediator, String name) {
		super(mediator, name);
		// 在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合]
		mediator.Register(name, this);
	}

	public void SendAlarm(int stateChange) {
		SendMessage(stateChange);
	}

	@Override
	public void SendMessage(int stateChange) {
		// 调用的中介者对象的 getMessage
		this.GetMediator().GetMessage(stateChange, this.name);
	}
}

测试类

public class ClientTest {

    public static void main(String[] args) {
        // 创建一个中介者对象
        Mediator mediator = new ConcreteMediator();

        // 创建 Alarm 并且加入到 ConcreteMediator 对象的HashMap
        Alarm alarm = new Alarm(mediator, "alarm");

        // 创建了 CoffeeMachine 对象,并  且加入到  ConcreteMediator 对象的 HashMap
        CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine");

        // 创建 Curtains,并且加入到  ConcreteMediator 对象的HashMap
        Curtains curtains = new Curtains(mediator, "curtains");
        TV tV = new TV(mediator, "TV");

        // 让闹钟发出消息
        alarm.SendAlarm(0);
        coffeeMachine.FinishCoffee();
        alarm.SendAlarm(1);
    }
}

中介者模式的注意事项和细节

  • 传统下多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
  • 中介者模式减少类间依赖,降低了耦合,符合迪米特原则
  • 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
  • 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意

中介者模式的应用场景

前面分析了中介者模式的结构与特点,下面分析其以下应用场景:

  • 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时
  • 当想创建一个运行于多个类之间的对象,又不想生成新的子类时