设计模式之【中介者模式】,多对多关系降级为一对多关系的利器

64 阅读12分钟

文章目录


全网最全最细的【设计模式】总目录,收藏起来慢慢啃,看完不懂砍我

一、什么是中介者模式

中介者模式(Mediator Pattern)又称为调节者模式或调停者模式。用一个中介对象封装一系列的对象交互,中介者使各对象不需要显式的相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互,属于行为型模式。

中介者模式的核心思想是,通过中介者解耦系统各层次对象的直接耦合,层次对象的对外依赖通信统统交由中介者转发。

1、中介者模式的优势

(1)松散耦合
中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一处而动全身”了。

(2)集中控制交互
多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么就扩展中介者对象,而各个同事类不需要做修改。

(3)一对多关联转变为一对一的关联
没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象以后,中介者对象和同事对象的关系通常变成一对多,这会让对象的关系更容易理解和实现。

2、中介者模式的缺点

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

3、中介者模式的应用场景

一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如在下图中,有六个同事类对象,假如对象1发生变化,那么将会有4个对象受到影响。如果对象2发生变化,那么将会有5个对象受到影响。也就是说,同事类之间直接关联的设计是不好的。
在这里插入图片描述
如果引入中介者模式,那么同事类之间的关系将变为星型结构,从下图中可以看到,任何一个类的变动,只会影响的类本身,以及中介者,这样就减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为。
在这里插入图片描述
中介者模式是用来降低多个对象和类之间的通信复杂性。这种模式通过提供一个中介类,将系统各层次对象间的多对多关系编程一对多关系,中介者对象可以将复杂的网状结构变成以调停者为中心的星型结构,达到降低系统的复杂性,提高可扩展性的作用。

若系统各层次对象之间存在大量的关联关系,即层次对象呈复杂的网状结构,如果直接让它们紧耦合通信,会造成系统结构变得异常复杂,且其中某个层次对象发生改变,则与其紧耦合的相应层次对象也需要进行修改,系统很难进行维护。而通过为该系统增加一个中介者层次对象,让其他各层次需对外通信的行为统统交由中介者进行转发,系统呈现以中介者为中心进行通讯的星型结构,系统的复杂性大大降低。

简单的说就是多个类相互耦合,形成了网状结构,则考虑使用中介者模式进行优化。总结中介者模式适用于以下场景:

  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解;
  • 交互的公共行为,如果需要改变行为则可以增加新的中介者类。

4、中介者模式的四大角色

在这里插入图片描述
中介者模式包含以下主要角色:

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

5、观察者模式和中介模式的区别

观察者模式有多种实现方式。虽然经典的实现方式没法彻底解耦观察者和被观察者,观察者需要注册到被观察者中,被观察者状态更新需要调用观察者的 update() 方法。但是,在跨进程的实现方式中,我们可以利用消息队列实现彻底解耦,观察者和被观察者都只需要跟消息队列交互,观察者完全不知道被观察者的存在,被观察者也完全不知道观察者的存在。

中介模式也是为了解耦对象之间的交互,所有的参与者都只与中介进行交互。而观察者模式中的消息队列,就有点类似中介模式中的“中介”,观察者模式的中观察者和被观察者,就有点类似中介模式中的“参与者”。

在观察者模式中,尽管一个参与者既可以是观察者,同时也可以是被观察者,但是,大部分情况下,交互关系往往都是单向的,一个参与者要么是观察者,要么是被观察者,不会兼具两种身份。也就是说,在观察者模式的应用场景中,参与者之间的交互关系比较有条理。

而中介模式正好相反。只有当参与者之间的交互关系错综复杂,维护成本很高的时候,我们才考虑使用中介模式。毕竟,中介模式的应用会带来一定的副作用,它有可能会产生大而复杂的上帝类。除此之外,如果一个参与者状态的改变,其他参与者执行的操作有一定先后顺序的要求,这个时候,中介模式就可以利用中介类,通过先后调用不同参与者的方法,来实现顺序的控制,而观察者模式是无法实现这样的顺序要求的。

二、实例

1、中介者模式的一般写法

// 抽象中介者
public abstract class Mediator {
    protected ConcreteColleagueA colleagueA;
    protected ConcreteColleagueB colleagueB;

    public void setColleageA(ConcreteColleagueA colleague) {
        this.colleagueA = colleague;
    }

    public void setColleageB(ConcreteColleagueB colleague) {
        this.colleagueB = colleague;
    }

    // 中介者业务逻辑
    public abstract void transferA();

    public abstract void transferB();
}

// 具体中介者
public class ConcreteMediator extends Mediator {
    @Override
    public void transferA() {
        // 协调行为:A 转发到 B
        this.colleagueB.selfMethodB();
    }

    @Override
    public void transferB() {
        // 协调行为:B 转发到 A
        this.colleagueA.selfMethodA();
    }
}
// 抽象同事类
public abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
}
// 具体同事类
public class ConcreteColleagueA extends Colleague {
    public ConcreteColleagueA(Mediator mediator) {
        super(mediator);
        this.mediator.setColleageA(this);
    }

    // 自有方法:self-Method
    public void selfMethodA() {
        // 处理自己的逻辑
        System.out.println(String.format("%s:self-Method", this.getClass().getSimpleName()));
    }

    // 依赖方法:dep-Method
    public void depMethodA() {
        // 处理自己的逻辑
        System.out.println(String.format("%s:depMethod: delegate to Mediator", this.getClass().getSimpleName()));
        // 无法处理的业务逻辑委托给中介者处理
        this.mediator.transferA();
    }
}
// 具体同事类
public class ConcreteColleagueB extends Colleague {
    public ConcreteColleagueB(Mediator mediator) {
        super(mediator);
        this.mediator.setColleageB(this);
    }

    // 自有方法:self-Method
    public void selfMethodB() {
        // 处理自己的逻辑
        System.out.println(String.format("%s:self-Method", this.getClass().getSimpleName()));
    }

    // 依赖方法:dep-Method
    public void depMethodB() {
        // 处理自己的逻辑
        System.out.println(String.format("%s:depMethod: delegate to Mediator", this.getClass().getSimpleName()));
        // 无法处理的业务逻辑委托给中介者处理
        this.mediator.transferB();
    }
}
// 测试类
public class Test {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
        ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
        colleagueA.depMethodA();
        System.out.println("-------------------------");
        colleagueB.depMethodB();
    }
}

2、租房中介案例

现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。

类图如下:
在这里插入图片描述


//抽象同事类
public abstract class Person {
	protected String name;
	protected Mediator mediator;
	public Person(String name,Mediator mediator){
		this.name = name;
		this.mediator = mediator;
	}
}
//具体同事类 房屋拥有者
public class HouseOwner extends Person {
	public HouseOwner(String name, Mediator mediator) {
		super(name, mediator);
	}
	//与中介者联系
	public void constact(String message){
		mediator.constact(message, this);
	}
	//获取信息
	public void getMessage(String message){
		System.out.println("房主" + name +"获取到的信息:" + message);
	}
}
//具体同事类 承租人
public class Tenant extends Person {
	public Tenant(String name, Mediator mediator) {
		super(name, mediator);
	}
	//与中介者联系
	public void constact(String message){
		mediator.constact(message, this);
	}
	//获取信息
	public void getMessage(String message){
		System.out.println("租房者" + name +"获取到的信息:" + message);
	}
}
//抽象中介者
public abstract class Mediator {
	//申明一个联络方法
	public abstract void constact(String message,Person person);
}
//中介机构
public class MediatorStructure extends Mediator {
	//首先中介结构必须知道所有房主和租房者的信息
	private HouseOwner houseOwner;
	private Tenant tenant;
	public HouseOwner getHouseOwner() {
		return houseOwner;
	}
	public void setHouseOwner(HouseOwner houseOwner) {
		this.houseOwner = houseOwner;
	}
	public Tenant getTenant() {
		return tenant;
	}
	public void setTenant(Tenant tenant) {
		this.tenant = tenant;
	}
	public void constact(String message, Person person) {
		if (person == houseOwner) { //如果是房主,则租房者获得信息
			tenant.getMessage(message);
		} else { //反正则是房主获得信息
			houseOwner.getMessage(message);
		}
	}
}
//测试类
public class Client {
	public static void main(String[] args) {
		//一个房主、一个租房者、一个中介机构
		MediatorStructure mediator = new MediatorStructure();
		//房主和租房者只需要知道中介机构即可
		HouseOwner houseOwner = new HouseOwner("张三", mediator);
		Tenant tenant = new Tenant("李四", mediator);
		//中介结构要知道房主和租房者
		mediator.setHouseOwner(houseOwner);
		mediator.setTenant(tenant);
		tenant.constact("需要租三室的房子");
		houseOwner.constact("我这有三室的房子,你需要租吗?");
	}
}

3、智能家具案例

智能家居包括各种智能设备,闹钟、咖啡机、电视、窗帘等,主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程未:闹钟响起->咖啡机开始做咖啡->窗帘自动落下->电视机开始播放。

//同事抽象类
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 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 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 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 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 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 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);
	}

}

三、源码中的中介者模式

1、Timer

JDK中的Timer类,有很多schedule()的重载方法:
在这里插入图片描述
并且schedule方法都调用了私有的sched方法:

private void sched(TimerTask task, long time, long period) {
    if (time < 0)
        throw new IllegalArgumentException("Illegal execution time.");

    // Constrain value of period sufficiently to prevent numeric
    // overflow while still being effectively infinitely large.
    if (Math.abs(period) > (Long.MAX_VALUE >> 1))
        period >>= 1;

    synchronized(queue) {
        if (!thread.newTasksMayBeScheduled)
            throw new IllegalStateException("Timer already cancelled.");

        synchronized(task.lock) {
            if (task.state != TimerTask.VIRGIN)
                throw new IllegalStateException(
                    "Task already scheduled or cancelled");
            task.nextExecutionTime = time;
            task.period = period;
            task.state = TimerTask.SCHEDULED;
        }

        queue.add(task);
        if (queue.getMin() == task)
            queue.notify();
    }
}

我们发现,最终会将任务加入到一个queue队列中顺序执行。我们把这个队列中的所有对象称为“同事”。同事之间通信都是通过Timer进行协调的,Timer就承担了中介者的角色。