zoukankan      html  css  js  c++  java
  • 设计模式的征途—22.中介者(Mediator)模式

    我们都用过QQ,它有两种聊天方式:一是私聊,二是群聊。使用QQ群,一个用户就可以向多个用户发送相同的信息和文件,从而无需一一发送,节省大量时间。通过引入群的机制,极大地减少系统中用户之间的两两通信,用户与用户之间的联系可以通过群的机制来实现。

    在有些软件中,某些类/对象之间的相互调用关系错综复杂,类似于QQ用户之间的关系,此时,特别需要一个类似“QQ群”一样的中间类来协调这些类/对象之间的复杂关系,以降低系统的耦合度。因此,一个设计模式因此诞生,它就是中介者模式。

    中介者模式(Mediator) 学习难度:★★★☆☆ 使用频率:★★☆☆☆

    一、客户信息管理模块的初始设计

    1.1 需求背景

    Background:M公司欲开发一套CRM系统,其中包含一个客户信息管理模块,所涉及的“客户信息管理窗口”界面效果图如下图所示:

    M公司开发人员通过分析发现,在上图中,界面组件之间存在较为复杂的交互关系:如果删除一个客户,则将从客户列表中删掉对应的项,客户选择组合框中客户名称也称将减少一个;如果增加一个客户信息,则客户列表中将增加一个客户,且组合框中也将增加一项。  

    1.2 初始设计

      M公司开发人员针对组件之间的交互关系进行了分析,发现:

      (1)当用户单击“增加”、“删除”、“修改”或“查询”时,界面左侧的“客户选择组合框”、“客户列表”以及界面中的文本框将产生响应。

      (2)当用户通过”客户选择组合框“选中某个客户姓名时,”客户列表“和文本框将产生响应。

      (3)当用户通过“客户列表”选中某个客户姓名时,“客户选择组合框”和文本框将产生响应。

      根据交互关系,开发人员绘制了如下图所示的初始类图。

      不难发现,上述设计存在以下问题:

      (1)系统结构负责且耦合度高 => 复杂的网状结构,OMG!

      (2)组件的可重用性差 =>  由于每一个组件和其他组件之间都具有很强的关联,很难重用!

      (3)系统的扩展性差 => 如果在上述系统中增加一个新的组件类,必须修改与之交互的各个组件源代码!

    二、中介者模式概述

    2.1 中介者模式简介

      如果在一个系统中对象之间存在多对多的相互关系,可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为了相对简单的一对多关系。通过引入中介者来简化对象之间的复杂关系,它是迪米特法则的一个典型应用。

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

    2.2 中介者模式结构

      在中介者模式中,引入了用于协调其他对象/类之间的相互调用的中介者类,为了让系统具有更好的灵活性和可扩展性,通常还提供了抽象中介者,其结构图如下图所示:

      中介者模式结构图中主要包含以下4个角色:

      (1)Mediator(抽象中介者):它定义了一个接口,该接口用于与各同事对象之间进行通信。

      (2)ConcreteMediator(具体中介者):它实现了接口,通过协调各个同事对象来实现协作行为,维持了各个同事对象的引用。

      (3)Colleague(抽象同事类):它定义了各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。

      (4)ConcreteColleague(具体同事类):抽象同事类的子类,每一个同事对象需要和其他对象通信时,都需要先与中介者对象通信,通过中介者来间接完成与其他同事类的通信。

    三、客户信息管理模块的重构实现

    3.1 重构后的设计结构

      M公司开发人员借助中介者模式来重新设计结构如下图所示:

      在具体实现时,为了确保系统有更好的灵活性和可扩展性,需要定义抽象中介者和抽象组件类,其中抽象组件类是所有具体组件类的公共父类,完整类图如下图所示:

      其中,Component充当抽象同事类,Button,List,ComboBox和TextBox充当具体同事类,Mediator充当抽象中介者类,ConcreteMediator充当具体中介类。在ConcreteMediator中维持了对具体同事对象的引用,为了简化ConcreteMediator类的代码,在其中只定义了一个Button对象和TextBox对象。

    3.2 重构后的代码实现

      (1)抽象中介者:Mediator

        /// <summary>
        /// 抽象中介者
        /// </summary>
        public abstract class Mediator
        {
            public abstract void ComponenetChanged(Component c);
        }

      (2)具体中介者:ConcreteMediator

        /// <summary>
        /// 具体中介者
        /// </summary>
        public class ConcreteMediator : Mediator
        {
            // 维持对各个同事对象的引用
            public Button addButton;
            public List list;
            public TextBox userNameTextBox;
            public ComboBox cb;
    
            // 封装同事对象之间的交互
            public override void ComponenetChanged(Component c)
            {
                // 单击按钮
                if (c == addButton)
                {
                    Console.WriteLine("-- 单击增加按钮 --");
                    list.Update();
                    cb.Update();
                    userNameTextBox.Update();
                }
                // 从列表框选择客户
                else if (c == list)
                {
                    Console.WriteLine("-- 从列表框选择客户 --");
                    cb.Select();
                    userNameTextBox.SetText();
                }
                // 从组合框选择客户
                else if (c == cb)
                {
                    Console.WriteLine("-- 从组合框选择客户 --");
                    cb.Select();
                    userNameTextBox.SetText();
                }
            }
        }

      (3)抽象同事类:Colleague

        /// <summary>
        /// 抽象同事类:抽象组件
        /// </summary>
        public abstract class Component
        {
            protected Mediator mediator;
    
            public void SetMediator(Mediator mediator)
            {
                this.mediator = mediator;
            }
    
            // 转发调用
            public void Changed()
            {
                mediator.ComponenetChanged(this);
            }
    
            public abstract void Update();
        }

      (4)具体同事类:ConcreteColleague

        /// <summary>
        /// 具体同事类:按钮组件
        /// </summary>
        public class Button : Component
        {
            public override void Update()
            {
                // 按钮不产生响应
            }
        }
    
        /// <summary>
        /// 具体同事类:列表框组件
        /// </summary>
        public class List : Component
        {
            public override void Update()
            {
                Console.WriteLine("列表框增加一项:张无忌");
            }
    
            public void Select()
            {
                Console.WriteLine("列表框选中项:小龙女");
            }
        }
    
        /// <summary>
        /// 具体同事类:组合框组件
        /// </summary>
        public class ComboBox : Component
        {
            public override void Update()
            {
                Console.WriteLine("组合框增加一项:张无忌");
            }
    
            public void Select()
            {
                Console.WriteLine("组合框选中项:小龙女");
            }
        }
    
        /// <summary>
        /// 具体同事类:文本框组件
        /// </summary>
        public class TextBox : Component
        {
            public override void Update()
            {
                Console.WriteLine("客户信息增加成功后文本框清空");
            }
    
            public void SetText()
            {
                Console.WriteLine("文本框显示:小龙女");
            }
        }
    
        /// <summary>
        /// 具体同事类:标签组件
        /// </summary>
        public class Label : Component
        {
            public override void Update()
            {
                Console.WriteLine("文本标签内容改变,客户信息总数量加1");
            }
        }

      (5)客户端测试:

        public class Program
        {
            public static void Main()
            {
                // Step1.定义中介者对象
                ConcreteMediator mediator = new ConcreteMediator();
    
                // Step2.定义同事对象
                Button addButton = new Button();
                List list = new List();
                ComboBox cb = new ComboBox();
                TextBox userNameTextBox = new TextBox();
    
                addButton.SetMediator(mediator);
                list.SetMediator(mediator);
                cb.SetMediator(mediator);
                userNameTextBox.SetMediator(mediator);
    
                mediator.addButton = addButton;
                mediator.list = list;
                mediator.cb = cb;
                mediator.userNameTextBox = userNameTextBox;
    
                // Step3.点击增加按钮
                addButton.Changed();
    
                Console.WriteLine("---------------------------------------------");
    
                // Step4.从列表框选择客户
                list.Changed();
            }
        }

      调试运行后的结果如下所示:

      

    四、中介者模式小结

    4.1 主要优点

      (1)简化了对象之间的交互,它用中介者和同事的一对多交互替代了原来同事之间的多对多交互

      (2)将各同事对象解耦,可以独立地改变和复用每个同事和中介者,增加新的中介和同事很方便,符合开闭原则。

      (3)可以减少大量同事子类的生成,改变同事行为只需要生成新的中介者子类即可。

    4.2 主要缺点

      具体中介者子类中包含了大量的同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。

    4.3 应用场景

      (1)系统中对象之间存在复杂的引用关系 => 系统结构混乱且难以理解

      (2)一个对象由于引用了其他很多对象并且直接和这些对象通信 => 难以复用该对象

      (3)想要通过一个中间类来封装多个类的行为又不想生成太多子类 => 引入中介者即可实现

    参考资料

      DesignPattern

      (1)刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

  • 相关阅读:
    Java 第十一届 蓝桥杯 省模拟赛 洁净数
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    20. Valid Parentheses
    290. Word Pattern
    205. Isomorphic Strings
    71. Simplify Path
  • 原文地址:https://www.cnblogs.com/edisonchou/p/7497234.html
Copyright © 2011-2022 走看看