隶属类别——对象行为型模式
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使耦合松散,而且独立改变他们之间的交互。
无
面向对象设计鼓励奖行为分布各个对象中。这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象。
虽然将一部分系统分割成许多对象通常可以增强可复用性,但是对象间相互连接的激增 又会降低其可复用性。大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作——系统表现为一个不可分割整体。而且,对系统的行为进行进行较大的改动都十分困难,因为行为被分布在许多对象中。结果是,你可能不得不定义许多子类以定制系统的行为。
例如,考虑一个图像用户界面中对话框的实现。对话框使用一个窗口来展现一系列的窗口,如按钮、菜单和输入域等,如下图所示:
通常对话框中的窗口组件间存在依赖关系。例如,当一个特定的输入域为空时,某个按钮不能使用;在称为列表框一列选项中选择一个表目可能会改变一个输入域的内容。反过来,在输入域中输入正文可能会自动的选择一个或多个列表框中相应的表目;一旦正文出现输入域中,其他一些按钮可能就变得能够使用了,这些按钮允许用户做一些操作,比如改变或者删除这些正文所指的东西。
不同的对话框会有不同的窗口组件间的依赖关系。因此即使对话框显示相同类型的窗口组件。也不能简单地重用已有的窗口组件类;而必须定制它们以反映特定对话框的依赖关系。由于涉及很多个类,用逐个生成子类的办法来定制它们会很冗长。
可以通过将集体行为封装在一个单独的中介者(mediator)对象中以避免这个问题。中介者负责控制和协调一组对象间的交互。中介者充当一个中介以使组中的对象不再相互显示引用。
例如,FontDialogDirectoor可作为一个对话框中的窗口组件中的中介者。FontDialogDirecotr对象知道对话框中的各窗口组件,并协调它们之间的交互,它充当窗口组件间的通信的中转中心,如下图所示。
下列交互图说明了各对象之间如何协作处理一个列表框中的选项变化。
注意Director是如何在对话框和入口域间进行中介的。而且窗口组件间的通信都通过Director间接地进行,它们不必互相知道;它们不必互相知道;它们仅需知道Director。而且,由于所有这些行为都保持在一个类中,只要扩展或替换这个类。
这里展示的是FontDialogDirector抽象怎样被集成到一个类库中,如下图所示:
DialogDirector是一个抽象类,它定义了一个对话框的总体行为。客户调用showDialog操作将对话框显示在屏幕上。createWidgets是一个创建一个对话框的窗口组件的抽象操作。widgetChanged是另一个抽象操作;窗口组件调用它来通知它的Director它们被改变了。DialogDirector的子类将重定义createWidgets创建正确的窗口组件。并重新widgetChanged以处理其变化。
在下列情况下适用Mediator模式:
一个典型的对象结构可能如下图所示
中介者模式有以下优点:
中介者模式有以下缺点:
下面是与中介者模式有关的一些实现问题:
另一个方法是在Mediator中定义一个特殊的通知接口,各Colleague在通信时直接调用该接口。Windows下的Smalltalk/V 使用某种形式的代理机制:当与Mediator通信时,Colleague将自身作为一个参数传递给Mediator,使其可以识别发送者。
本次使用Mediator模式实现一个聊天室
首先是Mediator——ChatMediator.java
public interface ChatMediator {
void addUser(User user);
void sendMessage(String massge, User user);
}
接下来是Colleague——User.java
public abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg, User sender);
public String toString() {
return name;
}
}
然后是ConcreteMediator——ChatMediatorImpl.java
public class ChatMediatorImpl implements ChatMediator {
List<User> users;
public ChatMediatorImpl() {
users = new ArrayList<>();
}
@Override
public final void addUser(User user) {
users.add(user);
}
@Override
public void sendMessage(String msg, User user) {
for (User u : users) {
if (u != user) {
u.receive(msg, user);
}
}
}
}
接下来是ConcreteColleague——UserImpl.java
public class UserImpl extends User {
public UserImpl(ChatMediator mediator, String name) {
// initialize base class
super(mediator,name);
}
@Override
public void send(String msg) {
System.out.println(this.name + " : " + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg, User user) {
System.out.println(this.name + " receiveMessage from " + user + " : " + msg);
}
}
最后是Client——ChatClient.java
public class ChatCilent {
public static void main(String[] args) {
// TODO Auto-generated method stub
ChatMediator mediator = new ChatMediatorImpl();
User user1 = new UserImpl(mediator, "饭团小神");
User user2 = new UserImpl(mediator, "唯爱伦科");
User user3 = new UserImpl(mediator, "hitman2");
User user4 = new UserImpl(mediator, "化腾土狗");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
mediator.addUser(user4);
user1.send("大家好,我是饭团小神,我的剑姬一秒四破");
user2.send("大家好,我是唯爱伦科,我的小炮贼猛");
user3.send("大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾");
user4.send("大家好,我是化腾土狗,我的妖姬可以出肉好吧。");
}
}
以及对于的实现结果:
饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
唯爱伦科 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
hitman2 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
化腾土狗 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
饭团小神 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
hitman2 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
化腾土狗 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
饭团小神 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
唯爱伦科 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
化腾土狗 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
饭团小神 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
唯爱伦科 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
hitman2 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
最后放上整个聊天室的类的UML图:
ET++ 和THINK C库都在对话框中使用类似与Director对象作为窗口组件间的中介者。
Windows下的Smalltalk/V的应用结构基于中介者结构。在这个环境中,一个应用有一个包含一组窗格(pane)的窗口组成。该类库包含若干预定义的Pane对象;比如说TextPane、ListBox、Button,等等。这些窗格无需继承即可直接使用。应用开发者仅需由ViewManager衍生子类,ViewManager类复杂窗格间的协调工作。ViewManager是有一个中介者,而每一个窗格只知道它的view mangaer,它被看作该窗格的“主人”。窗格不直接互相引用。
另一个中介者模式的应用是用于协调复杂的更新。一个例子是在Observer中提到的ChangerManager类。ChangeManager在subject和Observer间进行协调以避免冗余的更新。当一个对象改变时,它通知ChangeManager,ChangeManager随即通知依赖于该对象的那些对象以协调这些更新。
一个类似的应用出现在Unidraw绘图框架中,它使用一个称为CSolver的类来实现“连接器”间的连接约束。图形编辑器中的对象可用不同的方式表现出相同依赖。连接器用于自动维护连接的应用中,如框图编辑器和电路设计系统。CSolver是连接器的中介者。它解释连接约束并更新连接器的位置以反映这些约束。
《设计模式:可复用面向对象软件的基础》
《HeadFirst设计模式》