目录
- 一、场景
- 1、题目 【[来源](https://kamacoder.com/problempage.php?pid=1094)】
- 1.1 题目描述
- 1.2 输入描述
- 1.3 输出描述
- 1.4 输入示例
- 1.5 输出示例
- 二、不采用中介者设计模式
- 1 代码
- 2 问题
- 三、中介者设计模式
- 1 代码
- 2 更好的例子
- 四、个人思考
一、场景
- 设计模式不是银弹,而是特定场景的参考解法。
- 例如,在多人聊天室场景下,不采用中介者模式,系统会变得复杂。
1、题目 【来源】
1.1 题目描述
小明正在设计一个简单的多人聊天室系统,有多个用户和一个聊天室中介者,用户通过中介者进行聊天,请你帮他完成这个系统的设计。
1.2 输入描述
第一行包括一个整数N,表示用户的数量(1 <= N <= 100) 第二行是N个用户,比如User1 User2 User3,用空格分隔 第三行开始,每行包含两个字符串,表示消息的发出者和消息内容,用空格分隔
1.3 输出描述
对于每个用户,输出一行,包含该用户收到的所有消息内容。
1.4 输入示例
3
User1 User2 User3
User1 Hello_All!
User2 Hi_User1!
User3 How_is_everyone?
1.5 输出示例
User2 received: Hello_All!
User3 received: Hello_All!
User1 received: Hi_User1!
User3 received: Hi_User1!
User1 received: How_is_everyone?
User2 received: How_is_everyone?
二、不采用中介者设计模式
1 代码
- 将聊天室的人定义为User:
public class User {
private List<User> friends;
private String name;
public User(String name) {
this.name = name;
friends = new ArrayList<>();
}
public void addFriend(User user) {
friends.add(user);
}
public void sendMessage(String message) {
friends.forEach(user -> user.receiveMessage(message));
}
private void receiveMessage(String message) {
System.out.println(name + " received: " + message);
}
}
- 客户端:
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Map<String, User> userMap = new TreeMap<>();
int n = scanner.nextInt();
for (int i = 0; i < n; i++) {
String name = scanner.next();
User user = new User(name);
userMap.put(name, user);
}
List<User> users = new ArrayList<>(userMap.values());
users.stream().forEach(user -> {
for (User friend : users) {
if (!user.equals(friend)) {
user.addFriend(friend);
}
}
});
for (int i = 0; i < n; i++) {
String name = scanner.next();
String message = scanner.next();
userMap.get(name).sendMessage(message);
}
}
}
2 问题
- 现在聊天室里就3个人,只要1个人发消息,其他人都能收到。如果有人退群了,或者有新朋友加群了,都需要重新维护关系:
List<User> users = new ArrayList<>(userMap.values());
users.stream().forEach(user -> {
for (User friend : users) {
if (!user.equals(friend)) {
user.addFriend(friend);
}
}
});
- 当一个实体(例如:User)状态变化时,会影响其他实体,不应该由实体本身来维护这种复杂关系。实体只需关心自己的状态变化即可。
- 这就需要中介者设计模式。
三、中介者设计模式
1 代码
- 用户:
public interface Colleague {
void receive(String message);
String gotName();
}
public class User implements Colleague {
private final String name;
public User(String name) {
this.name = name;
}
@Override
public void receive(String message) {
System.out.println(name + " received: " + message);
}
@Override
public String gotName() {
return name;
}
}
- 中介者:
public interface Mediator {
void add(Colleague colleague);
}
public class ChatRoom implements Mediator {
private List<Colleague> colleagues;
public ChatRoom() {
this.colleagues = new ArrayList<>();
}
@Override
public void add(Colleague colleague) {
colleagues.add(colleague);
}
public void send(String message, Colleague colleague) {
colleagues.stream()
.filter(c -> !StringUtils.equals(c.gotName(), colleague.gotName()))
.forEach(c -> c.receive(message));
}
}
- 客户端:
public class Application {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ChatRoom chatRoom = new ChatRoom();
int n = scanner.nextInt();
for (int i = 0; i < n; i++) {
String name = scanner.next();
chatRoom.add(new User(name));
}
for (int i = 0; i < n; i++) {
String name = scanner.next();
String message = scanner.next();
chatRoom.send(message, new User(name));
}
}
}
2 更好的例子
- 廖雪峰老师在介绍中介者模式时,举的例子更好:
- 多选框会影响Select All、Select None的按钮状态。
- 例如,多选框勾选后,Select None按钮便会亮起:
- 多选框不应该去关心Select None按钮的状态,这就需要中介者模式来处理了。
- 多选框会影响Select All、Select None的按钮状态。
- 多选框状态变化后,中介者“收到变化消息”,如果需要更改按钮的状态,则去更改状态。
- 代码:
public class Mediator {
// 引用UI组件:
private List<JCheckBox> checkBoxList;
private JButton selectAll;
private JButton selectNone;
private JButton selectInverse;
public Mediator(List<JCheckBox> checkBoxList, JButton selectAll, JButton selectNone, JButton selectInverse) {
......
// 绑定事件:
this.checkBoxList.forEach(checkBox -> {
checkBox.addChangeListener(this::onCheckBoxChanged);
});
this.selectAll.addActionListener(this::onSelectAllClicked);
this.selectNone.addActionListener(this::onSelectNoneClicked);
this.selectInverse.addActionListener(this::onSelectInverseClicked);
}
// 当checkbox有变化时:
public void onCheckBoxChanged(ChangeEvent event) {
boolean allChecked = true;
boolean allUnchecked = true;
for (JCheckBox checkBox : checkBoxList) {
if (checkBox.isSelected()) {
allUnchecked = false;
} else {
allChecked = false;
}
}
selectAll.setEnabled(!allChecked);
selectNone.setEnabled(!allUnchecked);
}
// 当点击select all:
public void onSelectAllClicked(ActionEvent event) {
checkBoxList.forEach(checkBox -> checkBox.setSelected(true));
selectAll.setEnabled(false);
selectNone.setEnabled(true);
}
// 当点击select none:
public void onSelectNoneClicked(ActionEvent event) {
checkBoxList.forEach(checkBox -> checkBox.setSelected(false));
selectAll.setEnabled(true);
selectNone.setEnabled(false);
}
// 当点击select inverse:
public void onSelectInverseClicked(ActionEvent event) {
checkBoxList.forEach(checkBox -> checkBox.setSelected(!checkBox.isSelected()));
onCheckBoxChanged(null);
}
}
四、个人思考
- 当我们对场景建模后发现,实体们呈现类似如下关系时:
- 我们就需要引入中介来统筹实体们的相互影响。
- 实体1变化了,发消息给中介,中介收到后,去改变实体4和实体2的状态。
- 我们就需要引入中介来统筹实体们的相互影响。
- 中介者:
public interface Mediator {
...
}
public class xxx implements Mediator {
...
}
- 各种实体:
public interface Colleague {
...
}
public class yyy implements Colleague {
...
}