行为篇-中介者模式

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("早上好,三哥");

    

输出结果:
张三 对 李四 说:你好
李四 对 张三 说:早上好,三哥

问题:

  1. 这种设计虽然简单、直接,但双方在沟通前必须先建立连接,互相持有对方对象的引用,这样才能知道对方的存在。但如此便造成你中有我、我中有你,谁也离不开谁的状况,双方对象的耦合性太强。虽然在两人沟通的情况下,强耦合也不会造成太大问题,但是倘若我们要进行一场多方讨论的会议,那么在这种沟通模式下,每个参会人就不止是持有沟通对方这么简单了,而是必须持有其他所有人对象的引用列表(如使用ArrayList),以建立每个对象之间的两两连接。
  2. 对象间这种千丝万缕的耦合关系会带来很大的麻烦,当我们要加入或减少一个参会人时,都要将其同步更新给所有人,每个人发送消息时都要先查找一遍消息接收方,从而产生很多重复工作。我们陷入了一种多对多的对象关联陷阱,这让复杂的对象关系难以维护,所以必须重新考虑更合理的设计模式。

二、 构建交互平台

要解决对象间复杂的耦合问题,我们就必须借助第三方平台来把它们拆分开。首先要做的是把每个人持有的重复引用抽离出来,将所有人的引用列表放入一个中介类,这样就可以在同一个地方将它们统一维护起来,对引用的操作只需要进行一次。

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);
    

说明:

  1. 我们直接持有聊天室的引用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("瞧,王老五来了");
    

输出结果:
系统消息:欢饮【张三】加入聊天室【设计模式】
系统消息:欢饮【李四】加入聊天室【设计模式】
【张三的对话框】张三说:四兄弟,就你一个在啊
【李四的对话框】张三说:四兄弟,就你一个在啊
【张三的对话框】李四说:是啊,三哥
【李四的对话框】李四说:是啊,三哥
系统消息:欢饮【王五】加入聊天室【设计模式】
【张三的对话框】张三说:瞧,王老五来了
【李四的对话框】张三说:瞧,王老五来了
【王五的对话框】张三说:瞧,王老五来了

说明:

  1. 不管是谁发言,用户只需进入中介聊天室与其建立连接,即可轻松将消息发送至所有在线用户,消息以广播的形式覆盖聊天室内的每一个角落。聊天室中介平台的搭建,让用户以一种间接的方式进行沟通,彻底从错综复杂的用户直接关联中解脱出来。

三、 多态化沟通

我们已经实现了围绕聊天室展开的群聊系统。如果需要进一步增强功能就得继续对系统进行重构,例如用户可能需要一对一的私密聊天,或者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);



说明:

  1. 聊天室抽象类与用户类定义了一些基础的功能,对之前的代码进行了增强以完善系统功能,如聊天室类发送消息方法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;
    

说明:

  1. 公共聊天室除了可以广播式发送消息,还增加了发送消息给特定用户的功能。

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;
    

说明:

  1. 私密聊天室将加入人数限制为两人,沟通只在两人世界中展开。

5.超级用户类

public class AdminUser extends User
    public AdminUser(String name) 
        super(name);
    

    public void kick(User user)//踢出其他用户
        user.logout();
    

说明:

  1. 我们来定义一个超级用户类,让他拥有更多的权限。

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

说明:

  1. 至此,基于中介模式的聊天室多态化让系统功能越来越丰富了,我们将通用功能的公共代码抽象到了父类中实现,而对于个性化的功能则具体由子类去实现,并且让用户与平台各自负责自己的工作,类有所属,各尽其能。

总结

提示:这里对文章进行总结:`

  1. 中介模式不仅在生活中应用广泛,还大量存在于软硬件架构中,例如微服务架构中的注册发现中心、数据库中的外键关系表,再如网络设备中的路由器等,中介的角色均发挥了使对象解耦的关键作用。
  2. 中介模式的各角色定义如下:
  • Mediator(中介):共事者之间通信的中介平台接口,定义与共事者的通信标准,如连接注册方法与发送消息方法等。对应本章例程中的聊天室类ChatRoom(本例以抽象类的形式定义中介接口)。
  • ConcreteMediator(中介实现):可以有多种实现,持有所有共事者对象的列表,并实现中介定义的通信方法。对应本章例程中的公共聊天室类PublicChatRoom、私密聊天室类PrivateChatRoom。
  • Colleague(共事者)、ConcreteColleague(共事实现):共事者可以有多种共事者实现。共事者持有中介对象的引用,以使其在发送消息时可以调用中介,并由它转发给其他共事者对象。对应本章例程中的用户类User。

以上是关于行为篇-中介者模式的主要内容,如果未能解决你的问题,请参考以下文章

行为篇-中介者模式

中介者模式

行为型模式—中介者模式

行为型设计模式之中介者模式

设计模式 行为型模式 -- 中介者模式

中介者模式