学习设计模式第二十 - 中介者模式

示例代码来自DoFactory

 

概述

中介者模式出现的背景是,尽管将一个系统分割成许多对象通常可以增加其复用性,但是对象间相互连接的激增又会降低其可复用性。大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以,对系统的行为进行任何较大的改动就十分困难。

通过前面对设计原则的了解,我们知道迪米特法则正是解决上述问题的一个良方。在介绍设计原则中的迪米特法则时曾提到过中介者模式遵守这个法则,所以在真实设计中我们可以通过中介者模式来解决这个问题。通过该模式中的中介者对象我们可以将复杂系统交错的网状结构变成以中介者为中心的星形结构。

 

意图

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

 

UML

 

图1 中介者模式的UML图

 

参与者

这个模式涉及的类或对象:

  • Mediator

    • 定义一个与Colleague对象通信的接口

  • ConcreteMediator

    • 通过协调Colleague对象实现合作行为

    • 知道并管理其Colleague

  • Colleague系列类型

    • 每一个Colleague类了解其Mediator对象

    • 当一个需要与其它Colleague通行时,其应该与其Mediator通信

 

适用性

中介者模式定义一个提供对一组对象进行中心管理的对象,其通过封装这些对象的交互来实现。这种模式对于需要管理如每一个对象都需要了解组中其它每个对象状态改变这样复杂情况的场景很有用。

中介者模式常用于复杂对话框的开发。例如,你需要在一个对话框中输入选项来预订一个航班。一个简单的中介者规则应该是:你必须输入一个有效的出发日期,一个有效的返程日期,返程日期必须在出发日期后,一个有效的出发机场,一个有效的到达机场,一个有效地旅客号,只有这些都满足了搜索按钮才被激活。

另外中介者模式对存在复杂配置的场景也有帮助。Dell的网站提供了一个很好的例子。当为你的电脑选择定制的选项时,配置程序(中介者)持续跟踪你的选择。其主要任务是判断一个特定的硬件配件的组合是否可以很好的工作。例如,一个特别的显卡可能不能与一个Sony显示器一起工作。中介者就会标记出这些不兼容的情况。

下面是几种具体的中介者模式适用的场景。

  1. 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。

  2. 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。

  3. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

总结起来就是,中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。

 

DoFactory GoF代码

下面的示例代码展示了通过中介者模式为不同类型对象间实现松耦合通信提供便利。Mediator相当于一个交换中心,所有交互都应该在此进行。

// Mediator pattern 
// Structural example 
using System;
 
namespace DoFactory.GangOfFour.Mediator.Structural
{
    // Mainapp test application
    class MainApp
    {
        static void Main()
        {
            ConcreteMediator m = new ConcreteMediator();
 
            ConcreteColleague1 c1 = new ConcreteColleague1(m);
            ConcreteColleague2 c2 = new ConcreteColleague2(m);
 
            m.Colleague1 = c1;
            m.Colleague2 = c2;
 
            c1.Send("How are you?");
            c2.Send("Fine, thanks");
 
            // Wait for user
            Console.ReadKey();
        }
    }
 
    // "Mediator"
    abstract class Mediator
    {
        public abstract void Send(string message, Colleague colleague);
    }
 
    // "ConcreteMediator"
    class ConcreteMediator : Mediator
    {
        private ConcreteColleague1 _colleague1;
        private ConcreteColleague2 _colleague2;
 
        public ConcreteColleague1 Colleague1
        {
            set { _colleague1 = value; }
        }
 
        public ConcreteColleague2 Colleague2
        {
            set { _colleague2 = value; }
        }
 
        public override void Send(string message, Colleague colleague)
        {
            if (colleague == _colleague1)
            {
                _colleague2.Notify(message);
            }
            else
            {
                _colleague1.Notify(message);
            }
        }
    }
 
    // 'Colleague' abstract class
    abstract class Colleague
    {
        protected Mediator mediator;
 
        // Constructor
        public Colleague(Mediator mediator)
        {
            this.mediator = mediator;
        }
    }
 
    // "ConcreteColleague"
    class ConcreteColleague1 : Colleague
    {
        // Constructor
        public ConcreteColleague1(Mediator mediator)
            : base(mediator)
        {
        }
 
        public void Send(string message)
        {
            mediator.Send(message, this);
        }
 
        public void Notify(string message)
        {
            Console.WriteLine("Colleague1 gets message: " + message);
        }
    }
 
    // "ConcreteColleague"
    class ConcreteColleague2 : Colleague
    {
        // Constructor
        public ConcreteColleague2(Mediator mediator)
            : base(mediator)
        {
        }
 
        public void Send(string message)
        {
            mediator.Send(message, this);
        }
 
        public void Notify(string message)
        {
            Console.WriteLine("Colleague2 gets message: " + message);
        }
    }
}

 

这个实际应用的例子中,通过中介者模式协调不同参会者注册会议室的过程,使他们之间通信松耦合。会议室相当于交换中心,所有的通信都通过这里进行。这里的代码中会议室对象内仅实现了一对一的通信,但是将其改成一个一对多的通信系统也很容易。

例子中涉及到的类与迭代器模式中标准的类对应关系如下:

  • Mediator - IChatroom

  • ConcreteMediator - Chatroom

  • Colleague classes - Participant

// Mediator pattern 
// Real World example 
using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Mediator.RealWorld
{
    // MainApp test application
    class MainApp
    {
        static void Main()
        {
            // Create chatroom
            Chatroom chatroom = new Chatroom();
 
            // Create participants and register them
            Participant George = new Beatle("George");
            Participant Paul = new Beatle("Paul");
            Participant Ringo = new Beatle("Ringo");
            Participant John = new Beatle("John");
            Participant Yoko = new NonBeatle("Yoko");
 
            chatroom.Register(George);
            chatroom.Register(Paul);
            chatroom.Register(Ringo);
            chatroom.Register(John);
            chatroom.Register(Yoko);
 
            // Chatting participants
            Yoko.Send("John", "Hi John!");
            Paul.Send("Ringo", "All you need is love");
            Ringo.Send("George", "My sweet Lord");
            Paul.Send("John", "Can't buy me love");
            John.Send("Yoko", "My sweet love");
 
            // Wait for user
            Console.ReadKey();
        }
    }
 
    // "Mediator"
    abstract class AbstractChatroom
    {
        public abstract void Register(Participant participant);
        public abstract void Send(string from, string to, string message);
    }
 
    // "ConcreteMediator"
    class Chatroom : AbstractChatroom
    {
        private Dictionary<string, Participant> _participants = new Dictionary<string, Participant>();
 
        public override void Register(Participant participant)
        {
            if (!_participants.ContainsValue(participant))
            {
                _participants[participant.Name] = participant;
            }
 
            participant.Chatroom = this;
        }
 
        public override void Send(string from, string to, string message)
        {
            Participant participant = _participants[to];
 
            if (participant != null)
            {
                participant.Receive(from, message);
            }
        }
    }
 
    // "AbstractColleague"
    class Participant
    {
        private Chatroom _chatroom;
        private string _name;
 
        // Constructor
        public Participant(string name)
        {
            this._name = name;
        }
 
        // Gets participant name
        public string Name
        {
            get { return _name; }
        }
 
        // Gets chatroom
        public Chatroom Chatroom
        {
            set { _chatroom = value; }
            get { return _chatroom; }
        }
 
        // Sends message to given participant
        public void Send(string to, string message)
        {
            _chatroom.Send(_name, to, message);
        }
 
        // Receives message from given participant
        public virtual void Receive(string from, string message)
        {
            Console.WriteLine("{0} to {1}: '{2}'", from, Name, message);
        }
    }
 
    //" ConcreteColleague"
    class Beatle : Participant
    {
        // Constructor
        public Beatle(string name)
            : base(name)
        {
        }
 
        public override void Receive(string from, string message)
        {
            Console.Write("To a Beatle: ");
            base.Receive(from, message);
        }
    }
 
    //" ConcreteColleague"
    class NonBeatle : Participant
    {
        // Constructor
        public NonBeatle(string name)
            : base(name)
        {
        }
 
        public override void Receive(string from, string message)
        {
            Console.Write("To a non-Beatle: ");
            base.Receive(from, message);
        }
    }
}

 

.NET优化版代码实现了与上述代码相同的功能,但采用了更多现代化的.NET内置特性。上面例子中的抽象类由于没有任何实现代码在这里被接口代替。另外,泛型字典集合(Dictionary<T>)被用来以类型安全的方式存储Participant对象。最后,在例子中你也会看到.NET 3.0自动属性(C#)和类型初始化器的使用。

// Mediator pattern 
// .NET Optimized example 
using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Mediator.NETOptimized
{
    class MainApp
    {
        static void Main()
        {
            // Create chatroom participants
            Participant George = new Beatle { Name = "George" };
            Participant Paul = new Beatle { Name = "Paul" };
            Participant Ringo = new Beatle { Name = "Ringo" };
            Participant John = new Beatle { Name = "John" };
            Participant Yoko = new NonBeatle { Name = "Yoko" };
 
            // Create chatroom and register participants
            var chatroom = new Chatroom();
            chatroom.Register(George);
            chatroom.Register(Paul);
            chatroom.Register(Ringo);
            chatroom.Register(John);
            chatroom.Register(Yoko);
 
            // Chatting participants
            Yoko.Send("John", "Hi John!");
            Paul.Send("Ringo", "All you need is love");
            Ringo.Send("George", "My sweet Lord");
            Paul.Send("John", "Can't buy me love");
            John.Send("Yoko", "My sweet love");
 
            // Wait for user
            Console.ReadKey();
        }
    }
 
    // "Mediator"
    interface IChatroom
    {
        void Register(Participant participant);
        void Send(string from, string to, string message);
    }
 
    // "ConcreteMediator"
    class Chatroom : IChatroom
    {
        private Dictionary<string, Participant> _participants = new Dictionary<string, Participant>();
 
        public void Register(Participant participant)
        {
            if (!_participants.ContainsKey(participant.Name))
            {
                _participants.Add(participant.Name, participant);
            }
 
            participant.Chatroom = this;
        }
 
        public void Send(string from, string to, string message)
        {
            var participant = _participants[to];
            if (participant != null)
            {
                participant.Receive(from, message);
            }
        }
    }
 
    // "AbstractColleague"
    class Participant
    {
        // Gets or sets participant name
        public string Name { get; set; }
 
        // Gets or sets chatroom
        public Chatroom Chatroom { get; set; }
 
        // Send a message to given participant
        public void Send(string to, string message)
        {
            Chatroom.Send(Name, to, message);
        }
 
        // Receive message from participant
        public virtual void Receive(string from, string message)
        {
            Console.WriteLine("{0} to {1}: '{2}'",from, Name, message);
        }
    }
 
    // "ConcreteColleague"
    class Beatle : Participant
    {
        public override void Receive(string from, string message)
        {
            Console.Write("To a Beatle: ");
            base.Receive(from, message);
        }
    }
 
    // "ConcreteColleague"
    class NonBeatle : Participant
    {
        public override void Receive(string from, string message)
        {
            Console.Write("To a non-Beatle: ");
            base.Receive(from, message);
        }
    }
}

 

中介者模式解说

在一个中介者模式的系统中:

  • 每个对象都会在自己的状态改变时,告诉中介者。

  • 每个对象都会对中介者所发出的请求作出回应。

中介者模式的优点:

Mediator模式下减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator。

由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

通过将控制逻辑集中,可以简化系统维护。可以让对象之间所传递的消息变得简单而且大幅减少。

缺点:

由于ConcreteMediator控制集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂。当系统中新增任何一个需要与其他组件通信的组件时,中介者都要随之变化。

 

.NET中的中介者模式

就目前可知,.NET Framework的类库中没有使用中介者模式。

 

效果及实现要点

如果系统中只有一个中介者可以省略Mediator,直接使用ConcreteMediator对象。

 

总结

中介者模式将活动的组织者与参与者充分的解耦和。参与者可以独立灵活的变化,组织者也可以统一的控制所有参与者。

 

posted @ 2014-11-09 14:21  hystar  阅读(355)  评论(0编辑  收藏  举报