设计模式的艺术之道--中介者模式

来源:互联网 发布:快速减肥 知乎 编辑:程序博客网 时间:2024/05/19 00:16

设计模式的艺术之道–中介者模式

声明:本系列为刘伟老师博客内容总结(http://blog.csdn.net/lovelion),博客中有完整的设计模式的相关博文,以及作者的出版书籍推荐

本系列内容思路分析借鉴了刘伟老师的博文内容,同时改用C#代码进行代码的演示和分析(Java资料过多 C#表示默哀).

本系列全部源码均在文末地址给出。


本系列开始讲解行为型模式,关注如何将现有类或对象组织在一起形成更加强大的结构。

  • 行为型模式(Behavioral Pattern)
    关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责
    不仅仅关注类和对象本身,还重点关注它们之间的相互作用和职责划分
  • 类行为型模式
    使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责
  • 对象行为型模式
    使用对象的关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责

11种常见的行为型模式
这里写图片描述


中介者模式–协调多个对象之间的交互

使用QQ群,一个用户就可以向多个用户发送相同的信息和文件而无须一一进行发送,只需要将信息或文件发送到群中或作为群共享即可,群的作用就是将发送者所发送的信息和文件转发给每一个接收者用户。通过引入群的机制,将极大减少系统中用户之间的两两通信,用户与用户之间的联系可以通过群来实现。
QQ群其实就是用户之间的中介。

1.1定义

-中介者模式 (Mediator Pattern):定义一个对象来封装一系列对象的交互。中介者模式使各对象之间不需要显式地相互引用,从而使其耦合松散,而且让你可以独立地改变它们之间的交互。
- 通过引入中介者来简化对象之间的复杂交互。
- 对象之间多对多的复杂关系转化为相对简单的一对多关系。

1.2情景实例

问题描述
- 客户信息管理窗口的初始设计
开发团队需要开发一个客户信息管理模块,所设计的“客户信息管理窗口”界面效果图
这里写图片描述
界面组件之间存在较为复杂的交互关系:如果删除一个客户,要在客户列表(List)中删掉对应的项,客户选择组合框(ComboBox)中客户名称也将减少一个;如果增加一个客户信息,客户列表中需增加一个客户,且组合框中也将增加一项。
如何解决多个组件之间的交互?(组合框 客户列表 按钮 文本框)
初步思路分析
(1)当管理员用户单击“增加”按钮、“删除”按钮、“修改”按钮或“查询”按钮时,界面左侧的“客户选择组合框”、“客户列表”以及界面中的文本框将产生响应。
(2) 当管理员用户通过“客户选择组合框”选中某个客户姓名时,“客户列表”和文本框将产生响应。
(3) 当管理员用户通过“客户列表”选中某个客户姓名时,“客户选择组合框”和文本框将产生响应。

初步实例代码

//按钮类  class Button {      private List list;      private ComboBox cb;      private TextBox tb;      ......      //界面组件的交互      public void change() {          list.update();          cb.update();          tb.update();      }      public void update() {          ......      }      ......  } //列表框类  class List {      private ComboBox cb;      private TextBox tb;      ......  //界面组件的交互      public void change() {          cb.update();          tb.update();      }      public void update() {          ......      }      ......    } //组合框类  class ComboBox {      private List list;      private TextBox tb;      ......  //界面组件的交互      public void change() {          list.update();          tb.update();      }      public void update() {          ......      }      ......    }  //文本框类  class TextBox {      public void update() {          ......      }      ......    }  

现存缺点(未来变化)
(1) 系统结构复杂且耦合度高:每一个界面组件都与多个其他组件之间产生相互关联和调用
(2) 组件的可重用性差:实际使用时,每一个组件都能够单独重用,而不是重用一个由多个组件组成的复杂结构。
(3) 系统的可扩展性差:如果在上述系统中增加一个新的组件类,则必须修改与之交互的其他组件类的源代码

如何改进
为了协调界面组件对象之间的复杂交互关系,开发人员引入了中介者模式。
引入一个“第三者”来降低现有系统中类之间的耦合度。由这个“第三者”来封装并协调原有组件两两之间复杂的引用关系,使之成为一个松耦合的系统

UML类图
这里写图片描述
关键实例源代码

namespace MediatorSample{    abstract class Mediator    {        public abstract void ComponentChanged(Component c);    }      class ConcreteMediator : Mediator    {        //维持对各个同事对象的引用        public Button addButton;        public List list;        public TextBox userNameTextBox;        public ComboBox cb;        //封装同事对象之间的交互        public override void ComponentChanged(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();            }        }    }    abstract class Component    {        protected Mediator mediator;        public void SetMediator(Mediator mediator)        {            this.mediator = mediator;        }        //转发调用        public void Changed()        {            mediator.ComponentChanged(this);        }        public abstract void Update();    }    //其他类似的 省略  按钮 文本框     class ComboBox : Component     {        public override void Update()         {            Console.WriteLine("组合框增加一项:张无忌。");        }        public void Select()         {            Console.WriteLine("组合框选中项:小龙女。");        }    }     class Program    {        static void Main(string[] args)        {            //定义中介者对象            ConcreteMediator mediator;            mediator = new ConcreteMediator();            //定义同事对象            Button addBT = new Button();            List list = new List();            ComboBox cb = new ComboBox();            TextBox userNameTB = new TextBox();            addBT.SetMediator(mediator);            list.SetMediator(mediator);            cb.SetMediator(mediator);            userNameTB.SetMediator(mediator);            mediator.addButton = addBT;            mediator.list = list;            mediator.cb = cb;            mediator.userNameTextBox = userNameTB;            addBT.Changed();            Console.WriteLine("-----------------------------");            list.Changed();        }    }}

紧急增加新的需求

需求方提出了一个修改意见:要求在窗口的下端能够及时显示当前系统中客户信息的总数。
这里写图片描述
可以通过增加一个文本标签(Label)来显示客户信息总数,而且当用户点击“增加”按钮或者“删除”按钮时,将改变文本标签的内容。
对于中介者模式,有两种方式进行更改。
【解决方案一】增加一个界面组件类Label,修改原有的具体中介者类ConcreteMediator,增加一个对Label对象的引用,然后修改componentChanged()方法中其他相关组件对象的业务处理代码,原有组件类无须任何修改,客户端代码也需针对新增组件Label进行适当修改。
【解决方案二】与方案一相同,首先增加一个Label类,但不修改原有具体中介者类ConcreteMediator的代码,而是增加一个ConcreteMediator的子类SubConcreteMediator来实现对Label对象的引用,然后在新增的中介者类SubConcreteMediator中通过覆盖componentChanged()方法来实现所有组件(包括新增Label组件)之间的交互,同样,原有组件类无须做任何修改,客户端代码需少许修改。
使用解决方案二更加符合开闭原则。

更新的UML类图
这里写图片描述

更新实例源代码

namespace MediatorSample{    class Label : Component    {        public override void Update()         {            Console.WriteLine("文本标签内容改变,客户信息总数加1。");        }    }     class SubConcreteMediator : ConcreteMediator    {        //增加对Label对象的引用        public Label label;        public override void ComponentChanged(Component c)         {            //单击按钮            if (c == addButton)             {                Console.WriteLine("--单击增加按钮--");                list.Update();                cb.Update();                userNameTextBox.Update();                label.Update(); //文本标签更新            }            //从列表框选择客户            else if (c == list)             {                Console.WriteLine("--从列表框选择客户--");                cb.Select();                userNameTextBox.SetText();            }            //从组合框选择客户            else if (c == cb)             {                Console.WriteLine("--从组合框选择客户--");                cb.Select();                userNameTextBox.SetText();            }        }    }    class Program    {        static void Main(string[] args)        {            //用新增具体中介者定义中介者对象            SubConcreteMediator mediator;            mediator = new SubConcreteMediator();            Button addBT = new Button();            List list = new List();            ComboBox cb = new ComboBox();            TextBox userNameTB = new TextBox();            Label label = new Label();            addBT.SetMediator(mediator);            list.SetMediator(mediator);            cb.SetMediator(mediator);            userNameTB.SetMediator(mediator);            label.SetMediator(mediator);            mediator.addButton = addBT;            mediator.list = list;            mediator.cb = cb;            mediator.userNameTextBox = userNameTB;            mediator.label = label;            addBT.Changed();            Console.WriteLine("-----------------------------");            list.Changed();            Console.Read();        }    }}

1.3模式分析

动机和意图

  • 对象之间存在大量的多对多联系,将导致系统非常复杂,这些对象既会影响别的对象,也会被别的对象所影响(这些对象称为同事对象),组成了一个网状结构。在网状结构中,几乎每个对象都需要与其他对象发生相互作用,而这种相互作用表现为一个对象与另外一个对象的直接耦合,这将导致一个过度耦合的系统。
  • 如何降低这类型系统的耦合性?

一般结构

  • 中介者模式包含4个角色:
  • Mediator(抽象中介者):定义一个抽象的中介归类,包含对象的公共方法接口。
  • ConcreteMediator(具体中介者):具体的中介类,需要维持所有同事对象的引用。
  • Colleague(抽象同事类):抽象归类的同事类,需要关联中介类,包含公共方法的默认实现以及抽象的行为方法(子类实现不同)。
  • ConcreteColleague(具体同事类):具体同事类,实现父类的方法,通信是,先与中介类进行通信。

    中介者模式的核心在于中介者类的引入,在中介者模式中,中介者类承担了两方面的职责:
    (1) 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。
    (2) 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。

    中介者模式UML类图
    这里写图片描述

改进后的优点

  • 简化了对象之间的交互,使原来的网状关系变成了星型关系
  • 可将各同事对象解耦,只需保持与中介类的联系
  • 使得各个同事类可被重用,新的同时对象添加,只需增加新的中介类

现存的缺点

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

适用场景
(1) 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
(2) 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
(3)想通过一个中间类来封装多个类中的行为。
举例:微博平台 某某明星发生了绯闻 广大网友各自发表意见,开始点赞或者拉黑 ,喷人等等行为。
温室大棚的自平衡控制系统 水温 湿度 阳光 氧气 等因素变化。
植物的呼吸作用实验测试,氧气浓度,二氧化碳浓度,阳光的照射因素
现实应用中,可能对象之间的关系还不至于复杂到网状结构,要仔细分析实际情况。
实例源代码
GitHub地址
百度云地址:链接: https://pan.baidu.com/s/1boEchPH 密码: ik97

原创粉丝点击