zoukankan      html  css  js  c++  java
  • 软件设计模式修炼 -- 中介者模式


    对于那些存在对象之间复杂交互关系的系统,中介者模式提供了一种简化复杂交互的解决方案,即通过引入一个中介者,将原本对象之间的两两交互转化为每个对象与中介者之间的交互


    模式动机

    以微信聊天为例,可以用户与用户直接聊天,也可以通过微信群聊天。前者的话,用户要和别的用户加为好友,即用户和用户之间存在多对多关系,一个用户如果要将相同的信息发送给所有其他用户,必须一个一个发送。而如果使用群聊天,一个用户可以向多个用户发送相同信息而无须一一进行发送,只需将信息发送到群中即可,群的作用就是将发送者所发送的信息转发给每一个接收者用户。

    在用户与用户直接聊天的设计方案中,用户对象之间存在很强关联性,将导致系统出现如下问题:

    • 系统结构复杂
    • 对象可重用性差
    • 系统扩展混乱

    根据单一职责原则,我们应尽量将对象细化,使其只负责单一职责。一个由很多对象构成的模块,为了减少对象两两之间复杂的引用关系,我们需要使用中介者模式,这就是中介者模式的模式动机。


    模式定义

    用一个中介对象来封装一系列对象交互,中介者使各对象不需显式地相互引用,从而使其耦合松散,而且可以独立改变它们之间的交互。中介者模式又称调停者模式,它是一种对象行为型模式。


    模式结构

    1. Mediator(抽象中介者)

      定义一个接口,该接口应用于各同事对象之间的通信

    2. ConcreteMediator(具体中介者)

      抽象中介类的子类,通过协调各个同事对象来实现协作行为,了解并维护它对各个同事对象的引用。

    3. Colleague(抽象同事类)

      定义各同事的公有方法

    4. ConcreteColleague(具体同事类)

      每一个同事对象都引用一个中介者对象,每一个同事对象在需要时和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事的通信


    模式分析

    通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星状结构。在这个形状结构中,同事对象不再直接与另一个对象联系,它通过中介者对象与另一个对象发生相互作用。

    如果对象之间存在多对多的相互关系,可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行通信和协调,这样对象间的多对多的复杂关系就可以通过简单的多对一关系实现,符合迪米特法则,即“只与你直接的朋友们通信,而且只要可能,朋友数目越少越好”。

    中介者承担了以下两方面的职责:

    • 中转作用:通过中介者提供的中转作用,各个同事对象不需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可
    • 协调作用:中介者可以更进一步对同事之间的关系进行封装,同事一致地和中介者进行交互,而不需要指明中介者具体怎么做。中介者根据封装在自身内部的逻辑协调,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装

    模式实例之虚拟聊天室

    某论坛会员通过聊天室进行信息交流,普通会员(CommonMember)可以给其他会员发送文本信息,钻石会员(DiamondMember)既可以给其他会员发送文本信息,还可以发送图片信息。聊天室可以对不雅字符进行过滤,可以对发送图片的大小进行控制。

    1. 抽象中介者类 AbstractChatroom(抽象聊天室类)

      public abstract class AbstractChatroom {
          // 注册同事对象的方法
          public abstract void register(Member member);
          // 同事之间发送文本信息方法
          public abstract void sendText(String from, String to, String message);
          // 同事之间发送图片信息
          public abstract void sendImage(String from, String to, String image);
      }
      
    2. 抽象同事类 Member(抽象会员类)

      public abstract class Member {
      	// 维持一个抽象中介者的引用,用于调用中介者的方法
          protected AbstractChatroom chatroom;
          protected String name;
      
          public Member(String name) {
              this.name = name;
          }
      
          public AbstractChatroom getChatroom() {
              return chatroom;
          }
      
          public void setChatroom(AbstractChatroom chatroom) {
              this.chatroom = chatroom;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      	
          // 由于不同类型会员发送文本信息和图片信息的方式不同
          // 因此对这两个方法进行抽象声明
          public abstract void sendText(String to, String message);
      
          public abstract void sendImage(String to, String image);
      
          public void receiveText(String from, String message) {
              System.out.println(from + "发送文本给" + this.name + ",内容为:" + message);
          }
      
          public void receiveImage(String from, String image) {
              System.out.println(from + "发送图片给" + this.name + ",内容为:" + image);
          }
      }
      
    3. 具体中介者类 ChatGroup(具体聊天室类)

      import java.util.Hashtable;
      
      public class ChatGroup extends AbstractChatroom {
      	
          // 定义一个集合对象存储需要发生交互的同事对象
          private Hashtable<String, Member> members = new Hashtable<String, Member>();
      	
          // 提供注册方式,将同事对象加入集合中
          @Override
          public void register(Member member) {
              if (!members.contains(member)) {
                  members.put(member.getName(), member);
                  member.setChatroom(this);
              }
          }
      
          @Override
          public void sendText(String from, String to, String message) {
              Member member = members.get(to);
              String newMessage = message;
              //  模拟不雅字符过滤
              newMessage = message.replace("日", "*");
              // 调用会员接收方法
              member.receiveText(from, newMessage);
          }
      
          @Override
          public void sendImage(String from, String to, String image) {
              Member member = members.get(to);
              //  模拟图片大小判断
              if (image.length() > 5) {
                  System.out.println("图片太大,发送失败");
              } else {
                  // 调用会员接收方法
                  member.receiveImage(from, image);
              }
          }
      }
      
    4. 具体同事类 CommonMember(普通会员类)

      public class CommonMember extends Member {
      
          public CommonMember(String name) {
              super(name);
          }
      
          @Override
          public void sendText(String to, String message) {
              System.out.println("普通会员发送信息:");
              chatroom.sendText(name, to, message);
          }
      
          @Override
          public void sendImage(String to, String image) {
              System.out.println("普通会员不能发送图片!");
          }
      }
      
    5. 具体同事类 DiamondMember(钻石会员类)

      public class DiamondMember extends Member {
      
          public DiamondMember(String name) {
              super(name);
          }
      
          @Override
          public void sendText(String to, String message) {
              System.out.println("钻石会员发送信息:");
              chatroom.sendText(name, to, message);
          }
      
          @Override
          public void sendImage(String to, String image) {
              System.out.println("钻石会员发送图片");
              chatroom.sendImage(name, to, image);
          }
      }
      
    6. 客户端测试类 Client

      public class Client {
      
          public static void main(String[] args) {
      
              AbstractChatroom happyChat = new ChatGroup();
      
              Member member1, member2, member3, member4, member5;
              member1 = new DiamondMember("张三");
              member2 = new DiamondMember("李四");
              member3 = new CommonMember("王五");
              member4 = new CommonMember("小芳");
              member5 = new CommonMember("小红");
      
              happyChat.register(member1);
              happyChat.register(member2);
              happyChat.register(member3);
              happyChat.register(member4);
              happyChat.register(member5);
      
              member1.sendText("李四", "李四,你好");
              member2.sendText("张三", "张三,你好");
              member1.sendText("李四", "今天天气不错,有日");
              member2.sendImage("张三", "一个很大很大的太阳");
              member2.sendImage("张三", "太阳");
              member3.sendText("小芳", "还有问题吗?");
              member3.sendText("小红", "还有问题吗?");
              member4.sendText("王五", "没有了,谢谢");
              member5.sendText("王五", "我也没有了");
              member5.sendImage("王五", "谢谢");
          }
      }
      
    7. 运行结果

    如果需要增加新的具体中介类,只需继承抽象中介者类并实现其中方法即可,新的具体中介者类可以对信息进行不同的处理,客户端只需修改少量代码(如果使用配置文件的话可以不修改代码)

    如果增加新的同事类,继承抽象同事类并实现即可,同事类之间无直接引用关系。本实例中,中介者对同事类的引用建立在抽象层,因此在客户端实例化新增的同事类即可直接使用该对象


    模式优缺点

    中介者模式优点:

    1. 简化对象间的交互。用中介者和同事的一对多交互代替了原来同事之间多对多交互,更利于理解、维护和扩展。
    2. 将各同事解耦。我们可以独立的改变、增加和复用各同事和中介者,符合开闭原则
    3. 减少子类生成。中介者将原本分布于多个对象的行为集合在一起,改变这些行为只需生成新的中介者子类即可,各个同事类可被重用。

    中介者模式缺点:

    1. 具体中介者类包含了同事之间的交互细节,可能会导致中介者类非常复杂,难以维护。

    模式适用环境

    以下情况可以使用中介者模式:

    • 系统对象之间存在复杂的引用关系
    • 一个对象由于引用了其他很多对象并直接与其通信,导致难以复用该对象
    • 想通过一个中间类来封装多个类的行为,而又不想生成太多子类,可以使用中介者类,在中介者类中定义对象交互的公共行为

  • 相关阅读:
    字典序 动物统计 输出姓名和个数
    心急的C小加
    docker rabbitmq
    Docker Device Mapper 使用 direct-lvm
    FW docker使用问题总结,解决国内不能访问gcr.io的问题
    巧用AWK处理二进制数据文件
    Webshell清除-解决驱动级文件隐藏挂马
    phantomflow phantomcss, phantomas
    cobbler, puppet
    java javassis crack class
  • 原文地址:https://www.cnblogs.com/Yee-Q/p/12972120.html
Copyright © 2011-2022 走看看