行为篇-中介者模式
Posted zhixuChen333
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了行为篇-中介者模式相关的知识,希望对你有一定的参考价值。
文章目录
前言
中介是在事物之间传播信息的中间媒介。中介模式(Mediator)为对象构架出一个互动平台,通过减少对象间的依赖程度以达到解耦的目的。我们的生活中有各种各样的媒介,如婚介所、房产中介、门户网站、电子商务、交换机组网、通信基站、即时通软件等,这些都与人类的生活息息相关,离开它们我们将举步维艰。
提示:以下是本篇文章正文内容,下面案例可供参考
一、简单直接交互
通过中介我们可以更轻松、高效地完成信息交互。读者可能会提出这样的疑问:如果排除空间的限制,沟通人可以直接进行交互,根本不需要任何第三方的介入,中介显得有些多余。
为了更直观地理解中介的作用,我们用代码来模拟这种没有第三方参与的信息交互场景。
1.人类People
public class People
private String name;//已名字来区别
private People other;//持有对方的引用
public People(String name)
this.name = name;//初始化必须起名
public String getName()
return name;
public void connect(People other)
this.other = other;//连接方法中注入对方对象
public void talk(String msg)
other.listen(msg);
public void listen(String msg)
//聆听来自对方的声音
System.out.println(other.getName() + " 对 " + this.name + " 说:" + msg);
2.客户端类
public class Client
public static void main(String[] args)
People p3 = new People("张三");
People p4 = new People("李四");
p3.connect(p4);
p4.connect(p3);
p3.talk("你好");
p4.talk("早上好,三哥");
输出结果:
张三 对 李四 说:你好
李四 对 张三 说:早上好,三哥
问题:
- 这种设计虽然简单、直接,但双方在沟通前必须先建立连接,互相持有对方对象的引用,这样才能知道对方的存在。但如此便造成你中有我、我中有你,谁也离不开谁的状况,双方对象的耦合性太强。虽然在两人沟通的情况下,强耦合也不会造成太大问题,但是倘若我们要进行一场多方讨论的会议,那么在这种沟通模式下,每个参会人就不止是持有沟通对方这么简单了,而是必须持有其他所有人对象的引用列表(如使用ArrayList),以建立每个对象之间的两两连接。
- 对象间这种千丝万缕的耦合关系会带来很大的麻烦,当我们要加入或减少一个参会人时,都要将其同步更新给所有人,每个人发送消息时都要先查找一遍消息接收方,从而产生很多重复工作。我们陷入了一种多对多的对象关联陷阱,这让复杂的对象关系难以维护,所以必须重新考虑更合理的设计模式。
二、 构建交互平台
要解决对象间复杂的耦合问题,我们就必须借助第三方平台来把它们拆分开。首先要做的是把每个人持有的重复引用抽离出来,将所有人的引用列表放入一个中介类,这样就可以在同一个地方将它们统一维护起来,对引用的操作只需要进行一次。
1.用户类
public class User
private String name;//已名字来区别
private CharRoom charRoom;//聊天室的引用
public User(String name)
this.name = name;//初始化必须起名
public String getName()
return name;
public void Login(CharRoom charRoom)
this.charRoom = charRoom;//注入聊天室引用
this.charRoom.register(this);//调用聊天室连接注册方法
public void talk(String msg)//用户发现
charRoom.sendMsg(this, msg);
public void listen(User fromWhom, String msg) //用户聆听聆听
System.out.println("【" + this.name + "的对话框】" + fromWhom.getName() + "说:" + msg);
说明:
- 我们直接持有聊天室的引用chatRoom,并在用户登录方法login()中将其注入进来。接着调用聊天室的连接注册方法register()与其建立连接,这意味着用户不再与其他用户建立连接了,而是连接聊天室并告知“我进来了,请进行注册”。同样,发言方法talk()以及第聆听方法listen()也不与其他用户发生关联,前者会将消息直接发送给聊天室,后者则负责接收来自聊天室的消息。
2.聊天室类
public class CharRoom
private String name;//聊天室命名
List<User> users = new ArrayList<>();//加入聊天室的用户们
public CharRoom(String name)
this.name = name;//初始化必须命名聊天室
public void register(User user)
this.users.add(user);
System.out.println("系统消息:欢饮【" + user.getName() + "】加入聊天室【" + this.name + "】");
public void sendMsg(User fromWhom, String msg)
//循环Users列表,将消息发送给所有用户
users.stream().forEach(toWhom -> toWhom.listen(fromWhom, msg));
3.客户端类
public class Client
public static void main(String[] args)
CharRoom charRoom = new CharRoom("设计模式");
User user3 = new User("张三");
User user4 = new User("李四");
User user5 = new User("王五");
//张三、李四加入聊天室
user3.Login(charRoom);
user4.Login(charRoom);
//开始交谈
user3.talk("四兄弟,就你一个在啊");
user4.talk("是啊,三哥");
//王五进入聊天室
user5.Login(charRoom);
user3.talk("瞧,王老五来了");
输出结果:
系统消息:欢饮【张三】加入聊天室【设计模式】
系统消息:欢饮【李四】加入聊天室【设计模式】
【张三的对话框】张三说:四兄弟,就你一个在啊
【李四的对话框】张三说:四兄弟,就你一个在啊
【张三的对话框】李四说:是啊,三哥
【李四的对话框】李四说:是啊,三哥
系统消息:欢饮【王五】加入聊天室【设计模式】
【张三的对话框】张三说:瞧,王老五来了
【李四的对话框】张三说:瞧,王老五来了
【王五的对话框】张三说:瞧,王老五来了
说明:
- 不管是谁发言,用户只需进入中介聊天室与其建立连接,即可轻松将消息发送至所有在线用户,消息以广播的形式覆盖聊天室内的每一个角落。聊天室中介平台的搭建,让用户以一种间接的方式进行沟通,彻底从错综复杂的用户直接关联中解脱出来。
三、 多态化沟通
我们已经实现了围绕聊天室展开的群聊系统。如果需要进一步增强功能就得继续对系统进行重构,例如用户可能需要一对一的私密聊天,或者VIP用户需要具有超级权限等功能。这时我们就可以对聊天室与用户进行多态化设计,首先重构聊天室类与用户类。
1.聊天室抽象类和用户类
public abstract class CharRoom
protected String name;//聊天室命名
List<User> users = new ArrayList<>();//加入聊天室的用户们
public CharRoom(String name)
this.name = name;//初始化必须命名聊天室
protected void register(User user)
this.users.add(user);
protected void deregister(User user)
users.remove(user);//用户注销,从列表中删除用户
protected abstract void sendMsg(User from,User to, String msg);
protected abstract String processMsg(User from,User to, String msg);
说明:
- 聊天室抽象类与用户类定义了一些基础的功能,对之前的代码进行了增强以完善系统功能,如聊天室类发送消息方法sendMsg()的抽象化,再如用户类对发言方法talk()的改造。
2.用户类
public class User
private String name;//已名字来区别
private CharRoom charRoom;//聊天室的引用
public User(String name)
this.name = name;//初始化必须起名
public String getName()
return name;
public void Login(CharRoom charRoom) //用户登录
this.charRoom = charRoom;//注入聊天室引用
charRoom.register(this);//调用聊天室连接注册方法
protected void logout() //用户注销
charRoom.deregister(this);
this.charRoom = null;
public void talk(User to, String msg)
if (Objects.isNull(charRoom))
System.out.println("【" + name + "对话框】" + "您还没有登录");
return;
charRoom.sendMsg(this, to, msg);//给聊天室发送消息
public void listen(User from, User to, String msg) //用户聆听聆听
System.out.println("【" + this.getName() + "的对话框】" + charRoom.processMsg(from, to, msg));
@Override
public boolean equals(Object obj)
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return Objects.equals(name, user.name);
3.公共聊天室类
public class PublicChatRoom extends CharRoom
public PublicChatRoom(String name)
super(name);
@Override
protected void register(User user)
super.register(user);
System.out.println("系统消息:欢饮【" + user.getName() + "】加入公共聊天室【" + name + "】,当前人数:" + users.size());
@Override
protected void deregister(User user)
super.deregister(user);
System.out.println("系统消息:" + user.getName() + "离开公共聊天室【" + name + "】,当前人数:" + users.size());
@Override
protected void sendMsg(User from, User to, String msg)
if (Objects.isNull(to)) //如果接受者为空,则将消息发送给所有人
users.forEach(user -> user.listen(from, null, msg));
return;
//否则发送消息给特定的人
users.stream().filter(user -> user.equals(from) || user.equals(to)).forEach(user -> user.listen(from, to, msg));
@Override
protected String processMsg(User from, User to, String msg)
String toName = "所有人";
if (!Objects.isNull(to))
toName = to.getName();
return from.getName() + "对" + toName + "说:" + msg;
说明:
- 公共聊天室除了可以广播式发送消息,还增加了发送消息给特定用户的功能。
4.私人聊天室类
public class PrivateChatRoom extends CharRoom
public PrivateChatRoom(String name)
super(name);
@Override
protected synchronized void register(User user)
if(users.size() == 2)//聊天室最多容纳2人
System.out.println("系统消息:聊天室已满");
return;
super.register(user);
System.out.println("系统消息:欢饮【" + user.getName() + "】加入2人聊天室【" + name + "】");
@Override
protected void deregister(User user)
super.deregister(user);
System.out.println("系统消息:" + user.getName() + "离开2人聊天室【" + name + "】");
@Override
protected void sendMsg(User from, User to, String msg)
users.forEach(user -> user.listen(from, null, msg));
@Override
protected String processMsg(User from, User to, String msg)
return from.getName() + "说:" + msg;
说明:
- 私密聊天室将加入人数限制为两人,沟通只在两人世界中展开。
5.超级用户类
public class AdminUser extends User
public AdminUser(String name)
super(name);
public void kick(User user)//踢出其他用户
user.logout();
说明:
- 我们来定义一个超级用户类,让他拥有更多的权限。
6.客户端类
public class Client
public static void main(String[] args)
PublicChatRoom publicChatRoom = new PublicChatRoom("设计模式");
User user1 = new User("小明");
User user2 = new User("小红");
User user3 = new User("张三");
User user4 = new User("李四");
User user5 = new User("王五");
user1.Login(publicChatRoom);
user2.Login(publicChatRoom);
user3.Login(publicChatRoom);
user4.Login(publicChatRoom);
user5.Login(publicChatRoom);
user1.talk(user2, "你好,小红");
user3.talk(null, "各位抢红包啦!");
AdminUser admin = new AdminUser("管理员");
admin.kick(user5);
输出结果:
系统消息:欢饮【小明】加入公共聊天室【设计模式】,当前人数:1
系统消息:欢饮【小红】加入公共聊天室【设计模式】,当前人数:2
系统消息:欢饮【张三】加入公共聊天室【设计模式】,当前人数:3
系统消息:欢饮【李四】加入公共聊天室【设计模式】,当前人数:4
系统消息:欢饮【王五】加入公共聊天室【设计模式】,当前人数:5
【小明的对话框】小明对小红说:你好,小红
【小红的对话框】小明对小红说:你好,小红
【小明的对话框】张三对所有人说:各位抢红包啦!
【小红的对话框】张三对所有人说:各位抢红包啦!
【张三的对话框】张三对所有人说:各位抢红包啦!
【李四的对话框】张三对所有人说:各位抢红包啦!
【王五的对话框】张三对所有人说:各位抢红包啦!
系统消息:王五离开公共聊天室【设计模式】,当前人数:4
说明:
- 至此,基于中介模式的聊天室多态化让系统功能越来越丰富了,我们将通用功能的公共代码抽象到了父类中实现,而对于个性化的功能则具体由子类去实现,并且让用户与平台各自负责自己的工作,类有所属,各尽其能。
总结
提示:这里对文章进行总结:`
- 中介模式不仅在生活中应用广泛,还大量存在于软硬件架构中,例如微服务架构中的注册发现中心、数据库中的外键关系表,再如网络设备中的路由器等,中介的角色均发挥了使对象解耦的关键作用。
- 中介模式的各角色定义如下:
- Mediator(中介):共事者之间通信的中介平台接口,定义与共事者的通信标准,如连接注册方法与发送消息方法等。对应本章例程中的聊天室类ChatRoom(本例以抽象类的形式定义中介接口)。
- ConcreteMediator(中介实现):可以有多种实现,持有所有共事者对象的列表,并实现中介定义的通信方法。对应本章例程中的公共聊天室类PublicChatRoom、私密聊天室类PrivateChatRoom。
- Colleague(共事者)、ConcreteColleague(共事实现):共事者可以有多种共事者实现。共事者持有中介对象的引用,以使其在发送消息时可以调用中介,并由它转发给其他共事者对象。对应本章例程中的用户类User。
以上是关于行为篇-中介者模式的主要内容,如果未能解决你的问题,请参考以下文章