C#常用设计模式

来源:互联网 发布:网络配音 招聘 编辑:程序博客网 时间:2024/05/07 02:43

简单工厂模式

角色:用户,工厂,产品.

目的是使得用户将产品的消费和生产分开.在编程中就是将类的创建和使用分开.从而达到责任分离,其实这也是所有创建模式的目的之一.做法是工厂类根据用户的要求(参数)来返回不同的类的实例.

工厂实现:采用参数化的静态方法为用户提供类实例的创建,如下所示:

public static ProductType FactoryMethod(参数)

{

     根据参数返回需要类的实例.

}

简单工厂有几种情况:

1.产品只有一种,这种情况下方法没必要带参数.

2.产品有多种,且属于同类型产品,则可以加入抽象产品.加入抽象产品的一个潜在好处是可以让用户可以不关心具体的产品,在设计和实现上可以做到一定程度上的延后联编.

3.产品有多种,且类型各异,则可以考虑采用其它模式或者提供多个工厂方法来实现.但不鼓励采用后者来实现(做事要专业).

4.在实际应用中产品角色,工厂角色以及用户角色都可以根据实际情况合并.(模式变形)

简单工厂模式的理解:

1.如果产品创建的时候需要用户提供指导,则简单工厂模式不适合,如果要适用就会使得工厂方法的参数过于复杂,因此本模式只适合用户不关心产品构造逻辑,只关心使用的情况下(不关心构建,只关心使用).

2.大多数情况下用户还是必须知道产品的具体细节,没有减少对细节的依赖(特殊情况,有抽象产品的情况下,在某些应用中可以减少对具体产品的细节依赖).

3.采用静态方法实现类的创建,好处是可以减少工厂类本身的创建,但缺点是失去了工厂类进行继承扩展的优点.

4.简单工厂模式的好处是实现了责任分离,对用户来讲同时也隐藏了构建产品的细节,而且实现起来比较简单.

5.增加产品的时候,工厂类需要修改,也违背了面向对象的开放封闭原则.

6.只适合产品类比较少的情况,如果产品类太多,会使得静态工厂方法过于庞大.

 

但现在有一种比较简单的办法,就是采用反射或者泛型来规避静态方法过多而使得工厂类庞大的问题.


工厂方法模式

从简单工厂模式我们可以看出,在简单工厂模式中,增加新的产品类,是需要修改工厂类的,这不符合开放-封闭原则.而且在写工厂类的时候必须知道具体产品类的细节.也不符合由上相下的设计原则,如果要延后关注细节,则必须利用继承或者接口的技术来实现.将简单工厂模式中的工厂抽象,增加抽象工厂角色,这样用户,抽象工厂,抽象产品,用户就形成了最顶层的设计层面,具体的实现由具体的工厂来创建具体的产品.着就是工厂方法模式.

工厂方法模式角色:用户,抽象工厂,抽象产品,具体工厂,具体产品.

工厂方法模式采用一个具体工厂生产一种具体产品的方式进行产品的创建.用户具体要创建那种产品的选择逻辑推给了用户,用户通过选择不同的具体工厂来实现不同产品的创建选择.

工厂方法的优点:

1.符合开放封闭原则,因为增加新的产品不需要修改原来已有的工厂类,只需要增加相应的工厂即可.

2.将使用和创建的责任分开.

3.符合设计的先抽象后具体的顺序.

缺点:

1.如果产品类太多会导致增加很多的类,会使系统显得很不简洁.

2.如果将选择逻辑交给用户,则在一定程度上增加了用户对细节的依赖;

3.与简单工厂模式相比,增加了工厂本身的创建开销.

4.用户需要创建工厂类本身.(通过一定的方法可以将工厂类的创建委托给抽象类去完成.见例子.

抽象工厂方法是工厂方法更一般的表达,工厂方法可以针对产品族(如控件的Windows族控件,Unix族控件),如果只有一族产品(比如只有Windows族)则就退化成简单工厂方法(如果除掉抽象工厂,将工厂方法变为静态的方法,利用参数化减少工厂方法数量为1个,则就是简单工厂方法).)(一个工厂类,每个工厂方法创建一种产品).而如果只有一类产品(如只有Button控件),则退化成标准的工厂方法类似

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//变形的工厂方法
namespace MyBaseStudy
{

    abstract class AbstractFactory
    {
        //这种方法也需用户创建工厂类。
        public static AbstractProduct CreateProductM1(AbstractFactory bb)
        {
            return bb.CreateAProduct();
        }
        //利用模板方法将工厂类的创建委托给抽象工厂本身。
        public static AbstractProduct CreateProductM2<T>() where T : AbstractFactory,new()
        {
            T factory = new T();
            return factory.CreateAProduct();
        }
        public abstract AbstractProduct CreateAProduct();
    }
    abstract class AbstractProduct
    {
        public abstract double GetPrice();
        public abstract void  AddPrice(double p);
    }

    class ConcreteFactoryA : AbstractFactory
    {
        public override AbstractProduct CreateAProduct()
        {
            return new ConcreteProductA(100); 
        }
    }

    class ConcreteFactoryB : AbstractFactory
    {
        public override AbstractProduct CreateAProduct()
        {
            return new ConcreteProductB(200,0.9);
        }
    }
    class ConcreteProductA : AbstractProduct
    {
        double _price;
        public ConcreteProductA(double price)
        {
            this._price = price;

        }
        public override double GetPrice()
        {
            return this._price;
        }
        public override void  AddPrice(double p)
        {
            this._price += p + 1;
        }
    }
    class ConcreteProductB : AbstractProduct
    {
         double _price;
         double _rate;
        public ConcreteProductB(double price,double rate)
        {
            this._price = price;
            this._rate = rate;
        }
        public override double GetPrice()
        {
            return this._price;
        }
        public override void  AddPrice(double p)
        {
            this._price += p * this._rate;
        }

    }

   class Client
   {
       public static void Test()
       {
           //方法1,一般的使用方法,用户需要自己创建工厂类,要创建产品A

           AbstractFactory fa2 = new ConcreteFactoryA();
           AbstractProduct pa2 = fa2.CreateAProduct();
           System.Windows.Forms.MessageBox.Show(pa2.GetPrice().ToString());

           //方法2,一般的使用方法,用户需要自己创建工厂类,要创建产品A,这种方法跟1比没什么优势,
           //只是如果将CreateAProduct弄成保护类型,则可以隐藏创建细节,同时也可以起到一定代理模式的作用
           AbstractFactory fa1 = new ConcreteFactoryA();
           AbstractProduct pa1 = AbstractFactory.CreateProductM1(fa1);
           System.Windows.Forms.MessageBox.Show(pa1.GetPrice().ToString());

           //方法3,使用泛型方法方法,用户不需要自己创建工厂类,创建产品B,但这种方法的局限是工厂类必须具有不需要参数化的构造函数。
           AbstractProduct pb = AbstractFactory.CreateProductM2<ConcreteFactoryB>();
           System.Windows.Forms.MessageBox.Show(pb.GetPrice().ToString());

       }
   }
}
工厂方法和简单工厂一样,如果产品类太多都会导致具体的工厂类或方法过多,改进的办法是利用泛型,当然也可以利用反射,而且利用反射还可以进行参数化构造,但不利的地方是这同样会导致用户对构造细节的依赖.hibernate的构造方式其实就是这种方式,他将用户对构造细节的依赖转换成了可以动态配置的文件形式,而不是直接依赖.采用这种方式还有的好处就是工厂可以创造多种产品.但这种方式的一个缺点是需要采用动态参数构造目标产品的时候,这种方式就很难办到了.


简单工厂,工厂方法,抽象工厂之比较

创建模式的目的就是为了解决使用和生产责任分离,当然能使用这种模式的前提就是用户并不关心"产品"的创建逻辑和过程,用户只需要对创建的结果进行使用.在这种情况下,将使用和创建分离就增加了程序的灵活性,因为"产品"的构建对用户来说是透明的,如果需要对创建过程增加功能(比如增加创建权限检查),不会影响到用户的使用.

       模式的应用有两个基本的目的,一是复用,二是适应变化.而且这两个目的在很大程度上也是统一 的.工厂类模式将使用和创建责任分离,其实就是为了适应"创建"的变化.当然这种"适应变化"的代价就是需要增加工厂类,增加了系统的复杂度和开销.因此选择模式的一个重要的工作就是要权衡利弊,其实在创建模式中就有一个隐形假设:工厂角色的变化相对固定,工厂类本身的创建非常简单.这个假设很重要,因为如果工厂本身的创建和可变性都很大的话,采用这种模式就得不偿失了.

      简单工厂模式的原理其实很简单,就是将"产品"的创建工作委托给工厂类的静态方法来完成,有抽象产品的情况下,用户只需要用一个参数调用来获得"产品",而不需要知道具体的产品的细节,比如,抽象产品为水果,用户如果需要获取产品"苹果",则只要将"苹果"作为参数来调用水果工厂的创建水果的方法,而不需要知道苹果类本身的细节.这是简单工厂模式的好处.跟工厂方法相比,简单工厂模式逻辑简单,额外开销小(增加产品,不需要增加工厂类,因为是静态方法,工厂类本身也不需要创建等).

     工厂方法模式的原理也是将"产品"的创建委托给工厂类进行,但由于工厂类的创建方法不是静态的,因此可以利用继承的好处,将具体的创建工作"延时"到子类进行.这种方式在设计和思考上是有非常大的好处的:将设计分层,每一层都只需要关心同一层面上的事情.工厂方法的做法是每个具体产品都用一个具体的工厂来进行创建.这样做的好处是符合面向对象编程的开放封闭原则,增加新的产品,只需要增加新的工厂类即可(对扩展开放),对任何产品的增加或修改,不会影响其它工厂类和产品类(对修改封闭),但缺点也是非常明显的,就是产品多的时候,系统中的类会很多.(成倍增加).跟简单工厂模式相比,一是系统不简洁,二是增加了工厂类的创建,三是用户的选择产品逻辑变成了对工厂类的选择,降低了选择的直观性.

    抽象工厂方法可以看作是工厂方法的更一般的表达,抽象工厂针对的是产品族的概念(如果只有一种产品,就是工厂模式),如果按照抽象工厂的严格意义上来讲,应用场景会比较少(多种数据库支持,多种语言版本的情况可以用).能使用抽象工厂的应用场景一般可以表达为矩阵模型,同一行的是不同环境中的同一个产品的实现,同一列的产品都处于同一个应用环境下,系统每次只会用到一种环境的产品.典型的应用是Windows和Linux下的界面控件.如果只一族产品,则抽象工厂就可以退化成简单工厂来实现.

     在实际使用中,简单工厂模式和工厂方法模式用得比较多,而抽象工厂方法用得比较少.当然,抽象工厂在类似于数据库连接方面应用还是比较好的,比如系统如果需要支撑多种数据库系统,就可以采用抽象工厂来进行构建.

===============================

其实类的创建现在有了一些新的变化,例如,将创建依赖放在配置文件,采用反射技术来构造实例,这种方法的好处是一时可以减少工厂类,极端的情况下一个工厂方法就可以了,同时工厂类与产品类也脱耦。现在有很多框架都采用这种方式(如Nhibernate等),就是所谓的依赖注入,如下所示:

   public interface IPersonDao
    {
        int SaveData();
    }

    public class PersonDao : IPersonDao
    {
    #region IPersonDao 成员

        public int SaveData()
        {
          return 100;  
        }
        
        public PersonDao()
        {
        }
   #endregion
    }
    public class ObjectFactory
    {
        private static Dictionary<string, string> _objects = null;
        static ObjectFactory()
        {
            string theFile = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "MyObjects.xml";
            XElement root = XElement.Load(theFile);
            var objs = from obj in root.Elements("object")
                       select obj;
            _objects = objs.ToDictionary(k => k.Attribute("id").Value, v => v.Attribute("value").Value);
        }
        public static TRetObj GetObject<TRetObj>(string objectcode)
        {
            string theClassType = _objects[objectcode];
           
            Type theType = Type.GetType(theClassType, false, true);
            return (TRetObj)Activator.CreateInstance(theType);
        }
    }

MyObjects.xml

<?xml version="1.0" encoding="utf-8" ?>
<objects>
  <object id="PersonDao" value="SpringNetStudy.PersonDao,SpringNetStudy"/>
</objects>

BC:20120528
依赖配置的创建方式,本质上可以看作是简单工厂或者工厂方法,但这种方式的局限性也非常大:将用户选择产品的逻辑放在了配置文件里,一是配置文件的表达能力会比较弱,如果选择逻辑比较复杂的情况下,就无法适用.二是选择是在配置文件中,导致逻辑相对静态,无法实现动态的选择逻辑,这个局限根本原因也在于一.依赖配置创建的逻辑适合哪些创建逻辑简单,用户选择产品逻辑相对静态的,但需创建产品的数量比较大的情况下.


单例模式

单例模式的目的是保证类在系统中只被实例化一次,由该唯一的实例来为系统提供服务.单例模式主要用于保证服务的统一,比如获取统一的编号服务,模仿Oracle的序列生成等.但单例的使用需要谨慎,特别是在需要作负载均衡的地方,因为这种程序级的单例模式实际上只能保证在一个应用中为单例.如果被多个应用加载,还是会被多次实例化.

     同时如果采用懒汉式还需要注意线程安全.

     多例模式跟单例模式类似,但在实现上要复杂很多,需要维护自己的实例池和分配使用策略,还包括使用状态等的维护,多例模式主要用于有限个资源的管理,

比如数据库连接池等。

下面是单例模式例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//单例模式:就是保证系统中只实例化一个实例。
namespace MyBaseStudy
{
    //模式1
    class SingleTon_1
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
        private static SingleTon_1 _instance = new SingleTon_1();
        //在单例模式中需要将构造函数设为Protected或者private,防止其它类来使用构造函数进行实例化。
        private SingleTon_1()
        {
        }
        public static SingleTon_1 GetInstance()
        {
            return _instance;
        }
    }

    //模式2 称为懒汉式,需要保证线程安全
    class SingleTon_2
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
        private static SingleTon_2 _instance = null;
        private static readonly object padlock = new object();
        //在单例模式中需要将构造函数设为Protected或者private,防止其它类来使用构造函数进行实例化。
        private SingleTon_2()
        {
             
        }
        //这种缩开销比较大,因为每次进来都需要锁。
        public static  SingleTon_2 GetInstanceA()
        {
            lock (padlock)
            {
                if (_instance == null)
                {
                    _instance = new SingleTon_2();
                }
                return _instance;
            }
        }
        //只有在没有实例化的情况下才加锁。
        public static SingleTon_2 GetInstanceB()
        {
            if (_instance == null)
            {
                lock (padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new SingleTon_2();
                    }
                    
                }
            }
            return _instance;
        }
    }

    //一般类
    class CommClass
    {
        private double _value = 0;
        public double Value
        {
            get
            {
                return _value;
            }
            set
            {
                this._value = value;
            }
        }
    }
    //利用泛型来实现单例类的构建,好处是要使用单例模式的类本身可以是普通类,可用于复用。
    public sealed class SingleTon_3<T> where T : new()
    {
        
        public static T GetInstance()
        {
           
            return SingleTonCreator.Instance;
            
        }
        private class SingleTonCreator
        {
            internal static readonly T Instance = new T();
            static SingleTonCreator()
            {
            }

        }
    }

    public class SingleTonTest
    {
        public static void test()
        {
            SingleTon_1.GetInstance().Value++;
            MessageBox.Show(SingleTon_1.GetInstance().Value.ToString());

            SingleTon_1.GetInstance().Value++;
            MessageBox.Show(SingleTon_1.GetInstance().Value.ToString());

            SingleTon_2.GetInstanceA().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceA().Value.ToString());

            SingleTon_2.GetInstanceA().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceA().Value.ToString());

            SingleTon_2.GetInstanceB().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceB().Value.ToString());

            SingleTon_2.GetInstanceB().Value++;
            MessageBox.Show(SingleTon_2.GetInstanceB().Value.ToString());

            SingleTon_3<CommClass>.GetInstance().Value++;

            MessageBox.Show(SingleTon_3<CommClass>.GetInstance().Value.ToString());

            SingleTon_3<CommClass>.GetInstance().Value++;

            MessageBox.Show(SingleTon_3<CommClass>.GetInstance().Value.ToString());
        }
    }
}


生成器模式

在产品结构比较复杂,构造过程比较繁琐,一次性构造比较难的时候,我们可以采取分而治之的原则,将产品组件化,每个组件由专门的厂商来生产,最后的产品指派给制定的车间进行最后装配.这种方式其实是现代制造业的一种典型的模式.比如汽车,飞机的制造等.这样做的好处是:

    1.产品的部件由专门的生产厂商来生产,这样分工更加明确,更加精细,生产专业化,可以降低成本;

    2.整个产品的生产更加简单,也可增加零件的复用.

    3.转换生产比较容易,比如产品的材质发生变化,只需要找相应提供该材质的厂商即可.

这其实就是典型的生成器模式.生成器模式有4个角色:

A.抽象生成器:提供生成器的抽象表达,具体的生成由子类完成.抽象生成器可保持一个产品的引用;

B.具体生成器:每个具体的生成器提供一个对产品各个部分的构造方法.同时提供一个对最终产品进行访问的方法.

C.产品:需要构造的产品,该产品可以组件化来生产;

D.构造指导者:其实就是产品装配车间,构造过程和顺序都是固定的.

生成器模式的好处:

1.同其它创建型模式一样实现了产品的使用和创建的责任分离;

2.延迟了产品的构建过程;

3.可以通过指定不同的生成器来生成不同形式的产品;

4.将产品的构造组件化,降低了产品的生产和管理的难度.

抽象模式也可以完成上述功能,但抽象模式并不为专门是来构造复杂对象的.生成器模式更加关注一种产品本身的构造过程.

================================================================================

生成器模式一个比较典型而非常有用的应用就是构造sql语句,特别做一些动态查询业务的时候非常有用。比如:sql语句的构造可分为select部分构造,where条件构造,order by,group by 构造等部分。最后简单连接起来就形成完整的sql语句.


生成器模式与抽象工厂模式的比较

从形式上来讲,通过角色合并,方法功能的转变,抽象工厂可以和生成器模式形式上取得一致(比如抽象工厂只处理一个产品族,工厂方法都处理同一个产品)。但注意,这仅仅是形式上的,实际上,抽象工厂和生成器模式有着本质的区别:

1、生成器模式是为了构造一个复杂的产品,而且购造这个产品遵循一定的规则(相同的过程),而抽象工厂则是为了创建成族的产品(系列产品),同族产品的构造在逻辑上并不存在必然的联系(唯一必然的联系就是大家都属于一族)。

2、生成器模式的构造方法是为了构造同一个产品,因此必须有指导者来协调进行工作,构造方法之间存在必然的业务联系,而抽象工厂的构造方法都是独立去构建自己的产品对象,因此他们不存在必然的联系。在生成器模式中客户端不直接调用构建产品部分的方法来获取最终产品,而抽象工厂中客户端是通过调用不同的工厂方法获取不同的产品。

3.在生成器模式中,那些用来构造产品不同部分的方法一般都实现为Protected形式,以防止客户端通过调用这种方法活得不可预料的结果,而抽象工厂中的这些方法必须为Public形式。否则客户无法调用来获得产品结果;

4.生成器模式的角色有生成器,产品和指导者,而抽象工厂的角色有工厂和产品。无论角色和功能怎样变换,但所含的业务逻辑角色都应该存在,这也是两个模式的业务本质。


原型模式

原型提供了另外一种创建对象的方式:通过已有的对象的自身提供的克隆功能来创建新的对象。

这样做的好处很明显:

1.使用克隆来创建对象的用户不需要知道对象的构造细节;

2.性能上有利。

但采用克隆来生成对象也要注意需要克隆的内容,如果采用全复制的话,有些业务上可能会产生逻辑错误,比如对象ID冲突等。

克隆分为深度克隆和浅度克隆。一般来讲使用浅度克隆比较好,不会使得业务逻辑变得很复杂。

 

===============================================================

在实际开发中,对于实体类提供克隆方法有很多好处,比如,有的时候我们需要将获取的一系列实体修改部分关键字段,,然后插入到数据库中,如果当前获取的实体又不想改变,那么采用实体克隆再插入就非常方便.


桥模式

顾名思义,桥的作用就是将两条相互独立的路桥接到一起,而两条路可以保持各自的相对独立。在程序设计中,“桥”模式中的桥只是一种形象的比喻。根据桥模式的定义,桥模式实现了一个主题(可以是一个类,也可以是一中设计上的概念)的抽象部分和实现部分的分离(有的地方定义为抽象与具体行为的分离)。抽象部分的基类和实现部分的基类充就当着桥(接口对接)的作用,而抽象部分的子类和实现部分的子类就是桥两边的路,可以各自相对独立的发展(变化)(之所以是相对独立,从实现上来讲就是这种变化不能脱离最终能够实现桥接这个目标)。   

 我们知道,对于对象的研究,我们可以用这样的模型来表达对象:属性和行为。行为和属性都是对象的特征,其中属性描述对象的静态特征,而行为表达对象的动态特征,一般来讲,动态特征往往跟静态特征密切相关,相互影响,静态特征会约束动态特征的(行为),比如一个人如果是小孩(属性),那么他的行为很多都是受约束的,反过来,如果一个人经过吃(行为),又会长大,影响到身高,体重等(属性)。因为对象的特征都会发生变化,如果我们要表达这种变化,一是需要对这些对象根据我们的实际需要进行抽象,提取需要的特征,而对那些次要或者不需要的特征进行剔除。但这仅仅是研究对象的基础--抽象,我们还是需要表达重要特征的变化。

对于静态特征而言,变化的表达比较简单,只需要修改特征值即可,而且在实现上也比较容易,只要不同层次的对象都保持自己的特征值即可。但对于行为特征就不一样了,因为行为特征不仅受到静态特征这个约束,还会受到环境的影响。要用分类模式来表达所有静态特征和行为特征的变化是很困难的,我们只考察一种静态特征和一种行为特征的变化,这是最基本的一种情况,考虑到现有的技术体系,我们有两种方法来实现:

   A)利用类的单继承来实现(形成分类层次),比如人是抽象类,有个重要的特征性别,一个重要的行为跑,性别不同跑的行为当然也不一样,利用继承,我们可以派生出两个子类——男人和女人。这样行为跑也可以分为男人的跑和女人的跑,当然,如果仅仅是研究跑与性别的关系,问题是可以解决。但前面说了,行为除了受到静态特征的约束外,还会受到环境的影响,比如在不同产地的跑。这种情况下,如果还按照继承分类的方式去做,显然是不可取的,一是类体系太复杂,二是用环境来分类对象显然也不是一种可取的方式,因为环境是外在的。

   B)通过类的多继承来实现,对于多个特征的变化,这种方法可以减少类的数量,对于上述的例子,我们可以用一个对象的抽象和环境(比如产地)的抽象,人可以分为男人,女人,环境可以分为沙地,塑料跑道等。要描述男人在沙地跑,我们就从沙地和男人继承形成一个新的子类。这种方式看起来是可以,但有很多问题,一是不太符合对象分类原则(研究对象的分类一般都是根据对象本身,而不是会根据环境),在类体系上对象与环境天然就应该分离。二是从技术实现上来讲也比较麻烦,也违背了责任单一原则。

   那怎么来解决这种问题呢?我们来分析一下类的静态特征和动态特征,我们发现,对于静态特征的变化我们大多时候并不需要提供派生类来实现,只需要通过修改设置静态特征数据就可以了,只有那些对行为影响非常大,又属于我们主要研究范畴的静态特征,我们才需要按此属性进行派生,(比如,性别),而行为特征的变化,除了受到静态特征的影响外,还受到外界环境的影响,同时也会影响外界环境,比如人的跑这个行为在不同场地上跑是不一样的(比如沙地,水泥地),如果我们也采用继承的方式来实现这种变化,一是上面说的弊端,二从逻辑上我们也很难接受根据行为的环境来进行类派生的做法(虽然技术上是可以的),很难将一个跑在沙地和跑在水泥地人视为不同的类(人),但我们可以将场地进行抽象,将人在某个产地跑的行为委托给产地来实现,我们在人这个类中保持一个对场地的引用,而在跑的方法中除了实现自己必要的逻辑外,可以调用场地中的跑来具体实现跑的行为。因为双方各自可以采用继承的方式实现自己的变化,我们就可以在实际使用时临时指派具体场地。这样既减少了类的数量,又使得逻辑更加简单和灵活。下面是一个简单的例子类图:

从上面的例子我们可以看出,增加一个人的子类,或者增加一个产地,原来的结构都不需要动,符合设计的开闭原则。

总结如下:

A、桥模式实现了类中行为抽象与行为实现的分离,同时可以实现类本身和行为实现的各自发展。理论上对于任何特征都可以通过这种委托来实现抽象与实现的分离,但我觉得不推荐这种泛滥的应用,对于主要受静态特征影响而变化的行为我们可以通过继承来实现这种变化,只对受外在环境影响比较大的行为特征才使用这种模式来实现。

B.对于桥模式中抽象类保持一个对实现类的引用并不是必要的,对于上述例子,完全可以在通过Run的参数化来完成这种组合,这样人与产地的关系就可以从关联关系弱化到简单依赖。

C.桥模式提供了一种减少类的方法,为类沿着多维特征各自变化的情况提供了一种解决方法。

后记:桥模式可以实现系统在多个维度上的独立变化,这本身就要求系统在这些维度上的变化能够独立,如果这些维度相互之间存在着关联或者依赖,就会使得体系中关系过于复杂,相互之间的通信太多,甚至不能实现,比如人的行为跑,呼吸,说话,这几种行为相互影响有联系,就很难使用桥模式来实现,而在这种情况下就需要用建立数学模型的方式来进行。 


策略模式

如果一个类(CA)的方法存在不同的算法(F),而这些算法用户都可能用到,那么就可以将这种算法独立出来自我演变,既提供一个算法的抽象接口,再定义具体的算法类,而CA保持对算法的一个引用,CA中的方法不直接实现算法,而是调用用户所选的算法类中的算法来实现,这就是策略模式。也既定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

  *
  *                   | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                                  | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                   |    Context                   |                                                  |    Strategy                 |
  *                   |-----------------------------|◇-strategy---聚合------------→|-----------------------------|
  *                   |    Request                  |                                                  |   Handle()                  |
  *                   |________________|                                                   |________________|
  *                                                                                                                            △
  *                                                                                                          |---------------|------------|
  *                                                                                                          |                                   |
  *                                                                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                                                                     |  ConcreateStrategyA           |           |  ConcreateStrategyB      | 
  *                                                                     |---------------------------------- ----|           |-----------------------------------|
  *                                                                     |    Handle()                              |           |  Handle()                           |
  *                                                                     |_________________  ____|           |___________________|
  *                                                                      
  * 
    从上面UML图可以看出,从结构上来讲,策略模式跟桥模式很像,桥模式中抽象部分本身如果没有子类,那么桥模式就退化成了策略模式,相反如果策略模式中的Context类也可以独立演化,策略模式就进化成了桥模式。但从业务逻辑上来讲,这两个模式的应用目的是不一样的:桥模式侧重类本身和类行为实现部分各自独立发展,而策略模式则侧重不同算法的封装与支持。(其实很多模式之间的外观差异性很小,而且可以起到的作用也存在相似,这也是模式实际应用中很难区分到底是那个模式的原因)

    采用策略模式的时候需要注意几点:一是策略算法的实现要相对独立,既不依赖或者少依赖策略调用者(本图中的Context 类;二是不同策略之间要相互独立,且能相互替代。这点很关键,如果不同的策略不能相互替代,就会造成调用者调用的不确定性(在这点上,桥模式不要求这点)。

    另外如果策略算法依赖于调用者,则应该使用参数,而不要过多的暴露调用者本身的数据给策略类,因为暴露调用者的结构会造成策略类对调用者类的依赖,而相互依赖则增加了系统的耦合度。

    策略类的创建和Contex类的创建可使用其它创建型模式来完成。

    策略模式中的策略算法可能只是Context类中某个方法中的一个部分,在将策略算法中依赖的外界参数进行参数化后(由抽象策略提供标准的接口),策略算法就可以独立出来成为系统中的公共算法。

    策略模式从某种意义上来见,侧重于算法,但实际上策略模式可以应用于各个方面,比较极端的例子是使用委托来实现算法策略,这种方式更为灵活,一个比较典型的例子就是排序函数的比较策略,一般情况下实现几种默认的比较算法,但因为比较策略算法实在是太多,则可以通过委托调用来让用户自己去实现。这种方式的扩展性和灵活性都比较好。C#的查询扩展方法基本都是采用这种方式实现。而匿名函数和兰姆达表达式的引入,也使得这种策略实现的方便性大大争强.

   当然,利用委托充当策略抽象来实现策略算法最好用于那些多且不确定但算法简单的情况,对于算法复杂,算法比较固定的方式还是建议使用上述模式的标准方式。


装饰模式

装饰的含义就是在原有的物件上增加饰品,使得物件能呈现出不同的特征。当然在类设计中,采用装饰模式,是用来为类增加新的特征和行为。结构如下:

    *   
  *                   | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                
  *                   |     Component           |<-------------------------------------------------------------------|  
  *                   |----------------------- -----|                                                                                       |
  *                   | Operation()               |                                                                                       |                      
  *                   |________________|                                                                                        |
  *                                   △                                                                                                        |
  *                                     |                                                                                                          |
  *                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                                 |     
  * | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|◇-Component------|
  * |  ConcreateComponent       |           |     Decorator                          |
  * |--------------------------------------|           |---------------------------------------|      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  * |   Operation()                         |           |    Operation()  ○---------------|--→| Component-> Operation()  |
  * |_________________  ___|           |______________________|      |_____________________| 
  *                                                                                      △                                             
  *                                                                                       |                                             
  *                                                     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                
  *                      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|           | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                      |  ConcreateDecoratorA       |           | ConcreateDecoratorB         |
  *                      |--------------------------------------|           |---------------------------------------|      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *                      |   Operation()                         |           |    Operation()     ○-------------|-→| Decorator:: Operation()      |
  *                      |   addedState                        |           |   AddedBehavior()                |      | AddedBehavior()                | 
  *                      |_____________________|           |______________________|      |_________________ ___|
  * 
角色:

      抽象构件(Component):给出一个抽象接口,以规范准备接收附加责任的对象。
      具体构件(Concrete Component):定义一个将要接收附加责任的类。
      装饰(Decorator):持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。
      具体装饰(Concrete Decorator):负责给构件对象“贴上”附加的责任。

装饰模式有两种实现模式,一是如上图所示采用委托的方式进行,二是采用子类化的方式进行,但从装饰的本意来讲还是采用委托的方式比较好。采用委托的方式可以动态,灵活的为对象增加新的责任和功能。

总结:

A)装饰模式并不改变构件原有的行为和特征,只是增加新的行为和特征,与适配器模式不同,装饰者只改变对象的责任,而非接口。而适配器模式让对象有一个全新的接口。

B) 虽然装饰模式可以视为组合模式只有一个组件的特例,但装饰者主要是为了附加责任而非对象聚合(aggregation)。
C)装饰模式和策略模式都可以让你改变对象,但装饰者模式只改变对象的外表,而策略模式是改变对象的内部。

D)采用子类化本身就可以增加新的特征和行为,但采用这种方式在需增加的特征和行为比较多样化的情况下,会使得类体系过于庞大,而装饰模式因为装饰和构件是组合方式进行,因此不仅灵活,而且也可以减少类的数量。而且不是所有情况下都可以采用子类化的方式进行(比如具体构件是密封类)

E)装饰模式比静态继承更灵活;而且可以避免在层次结构较高的类具有太多的特征;但缺点是会产生很多小对象(饰件);虽然Decorator从Component继承,但其本身与Component不是同一种对象,因此在命名上应特别注意,不要让用户产生误会。

F)装饰模式和桥模式都可以用来减少类(如果构件本身没有演化,或者演化很简单,则装饰模式并不能家少类,反而会增加类)的数量,都可以避免复杂的继承结构,。但也有如下区别:

1)在结构上两者有区别(比较图就知道)

2)实现方式上不同:装饰模式是通过将构件的核心功能和附加功能分离,类本身体系独自变化,而将附加性功能交给装饰子类,这样核心功能和附加功能各自独立变化,通过装饰子类对构件的封装来实现动态的为组建提供附加的功能。而桥模式则是将抽象行为的实现细节独立出来,构造一个实现化结构,通过抽象体系结结构和实现结构体系的组合达到适应变化的目的。

3)而且应用中,不同的装饰模式可以同时使用(比如对于对于图片,可以加边框,加滤境。而桥模式中的不同具体实现则不能同时应用。

4)装饰件是独立的,构件部分可以不使用饰件,而桥模式中实现部分只是相对独立,抽象部分必须依赖实现部分进行工作。

装饰模式也是包装模式的一种。

 

后记:虽然在装饰模式中我们不建议使用子类化方式来实现,但在实际应用中,有的时候我们还是需要用这种方式来实现,因为利用委托的方式来实现,装饰只能特定于某类构件,在我们需要装饰一些列类型的构件时,就无法实现。虽然采用子类化来实现大量增加装饰类,而且如果要重复装饰类的增加量就更大,但由于动态编译和动态中间语言指令注入的使用,这些子类可以动态生成,这就为所谓的AOP(面向方面编程)提供了一种可能。实际上Spring.Net 的AOP编程就是利用了装饰模式,而且是子类化。原因非常简单这种场景需要增加附加责任,但同时要求装饰必须与构件保持一致接口。Spring里面的AOP就是装饰模式+动态代码注入(动态编译或者利用EMIT)的利用,当然这种利用也进行了改良,通过结合观察者模式,将要附加的责任类作为观察者注入,这样,装饰类仅仅是按照一定的规则调用需要增加的责任类,大大减少了装饰类本身的数量,同时也增加了灵活性,不过这种情况下,装饰类本身的执行逻辑就变得复杂起来,特别是对于前置通知的处理,如果前置通知会影响是否真正执行具体任务类责任方法,在多个这样的前置通知下是一票否决制度,还是选票制度,就很难选择,而且如果具体责任类与通知类耦合很紧密的话,也不适合这种模式。Spring.net里的AOP注入虽然已经考虑得比较全面,但从可控性来讲,要完成这种横切目标最好的办法是在设计的时候就考虑到,而采用虚模式或其它方式来实现,不一定非得去使用AOP模式。一句话就是不要为了应用一种技术而应用一种技术。


组合模式

有时候我们需要维护一些对象,这些对象具有一定的层次结构,它们之间虽然差异很大,但在使用的时候我们希望能构一致的对待,比如Windows控件,绘图中的图形等,这个时候我们就可以采用组合模式来进行管理。组合模式就是将要管理的对象按树型结构来进行组织,表示成一种“整体-部分”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。组合模式的结构图如下:


角色:

   组件接口:提供一致的访问接口,注意组件接口可以是抽象类,也可以是具体类,还可以用接口实现;

   组合对象:含有孩子对象的对象;

   叶子对象:不含孩子对象的对象。

组合对象自己维护一个子对象池,并按照组件接口的要求提供一致的访问方式,组合对象充当容器类职责,而叶子对象则不需要维护 子对象池,但也必须按组件接口要求实现统一的访问处理方式。

使用场景:

A)你想表示对象的“部分-整体”层次结构;

B)你希望用户忽略单个对象合组合对象的不同,用户将统一的使用组合结构中的所有对象;
典型的应用包括Delphi中的Component,C#中的控件Control,画图中的图形表示,业务中的组织结构表示,物料BOM,系统的功能结构,HTML的DOM模型等等。

与其它模式的区别:

     从结构图上来看组合模式和装饰模式很相近,相同之处是两种模式都提供了一种组合,装饰模式提供的是一种功能的组合,而组合模式提供的是一种对象组合。不同之处在于装饰模式只维护一个构件的引用,而组合模式中的组合对象提维护的是一个组件池,数量上有区别,这个区别就决定了装饰模式并不能构成树型结构,而组合模式则可以。  从业务逻辑上来讲,装饰模式的使用是为了给构件增加新的行为特征,而组合模式是为了整体-局部的层次型管理。从应用上来讲,组合模式的应用比装饰模式更具一般化,因此使用更广。

     另外装饰模式中装饰者和构件并不一定要求同一类对象,在使用上也有区别,而组合模式中的组合对象和叶子对象都是同一类对象,在使用上并无区别。

     组合模式的设计方法不仅在程序设计上应用非常广,在业务设计上,数据结构设计上都应用非常广泛。

     在使用组合模式时,为了提供更灵活的访问,在接口组件提供对父节点的访问接口,叶子对象和组合对象都会实现这种接口,比如Delphi中Wincontrol类中的Parent属性,C#中Control类中的Parent等。还可以提供对象检索之类的功能。

     在具体实现组合模式时,有时候根据需要,组合对象的子对象池只保持一个对子对象的引用,所有的对象会用一个集合来进行管理,这样做的好处主要是为了方便对对象的检索和管理。比如索引对象,释放资源。树型访问有的时候会比较慢。典型应用就是Delphi中的Form类。所有在Form中创建并指定了Owner属性的控件,都在Form的Components列表中。


适配器模式

在日常生活中,我们时常会遇到一些兼容性的问题,比如我的笔记本的电源插头就是欧标的,在很多场合我都没法用,买一个可以用的插座当然是可选手段,但到那里去都拿这么大个家伙一是不方便,二也未必有地方可以插,所以我只好买个电源适配插座带在身上。再比如电脑的输入电压一般都只有20伏左右,而日常电源基本都是220伏,所以就需要一个电源适配器,来降低电压。这些都是为了解决不同标准之间的兼容性问题。设计模式中的适配器模式就是我们在设计中用来解决标准兼容性问题的一种重要手段。适配器的角色分为适配器,适配对象和适配目标。适配对象是具体功能的执行者,但因接口原因用户无法直接调用,适配目标是用户希望的调用接口,适配器就继承适配目标,实现目标接口,接口的具体执行由保持的适配对象来执行。

适配器的应用场景:

1)需要使用一个已经存在的类,但这个类的接口标准不符合你的要求。分两种情况,一是老的系统中的类,但又不能修改这个类的接口(有的系统还需要使用,或者没有源代码),而现在又定义了新的标准。这在系统升级过程中很常见,二是该类属于第三方的类,可能你不想修改,或者也没法修改。这两种情况都要求你定义一个适配器类来解决这种不同标准时间的兼容问题;

2)需要定义一个能够被复用的类,而这个类能够与其它不相关的类或者不可预见的类一起协同工作,一个实现多个接口的类的目的往往就如此;

适配器的分类:

1)类适配器:适配器既实现目标接口,又适配对象继承。适配器在目标接口的实现中调用原对象的接口,达到接口转接的目的。但注意在类适配器模式中,目标接口只能用接口表示,不能采用类表示,因为这样会造成多继承出现。类适配器的好处是为适配对象增加部分功能比较容易(适配器作为适配对象子类,可以很方便的实现适配对象的访问)

2)对象适配器:适配器实现目标接口,但不从适配对象继承,而是维护一个对适配对象的引用,这样做的好处是在需要使用一些已经存在的子类,但是不可能对每个类都进行子类化以匹配它们的接口的时候,对象适配器可以通过适配
  它们的父类接口来实现对一系列子类的接口匹配,这也是类适配器不能做到的地方。但对象适配器也有缺点,因为是维护对适配对象的引用,而不是继承,因此在需要为适配对象增加功能时变得比较困难。

    对象适配器一般比较简洁,而类适配器则比较臃肿,因为大多时候采用适配器的地方,并不需要知道适配对象的很多细节,需要适配的接口都比较单一,这个时候采用对象适配器会比较好。类适配器不仅适配了接口,同时也继承了适配对象的很多其它功能,而且很容易暴露适配对象的细节。

与其它模式的关系:

1)与装饰模式相比,适配器模式是为了适配对象的接口,而装饰模式是为了增强对象的功能。装饰模式可以实现递归调用,而适配器模式则不能(对象适配器如果适配器是抽象的则可以,但一般不这么做,如果需要可以采用其它模式实现)。

2)桥模式和对象适配器有类似结构,但目的不一样,桥模式是为了接口与实现分隔开来以便利改变并使其不相关(抽象部分和实现部分各自独立变化),而对象型适配器的目的则是改变现有对象的接口。

3)代理模式的目的是为对象增加一个控制访问的中间代理,但它并不改变对象的接口,而适配器模式则是为了改变对象的接口,以适应新的接口标准,而不是为了增加附加性的功能(虽然也可以)。

另外,装饰模式,代理模式,适配器模式的作用比较相近,都是为了改变或增加对象的功能或者接口,而组合模式则是为了一致对待对象,同时提供了一种对象的组织方式。严格的将,在几个结构型模式中只有组合模式是完全意义上的结构模式。

 

后记:适配器的模式的关键在于接口适配。在新的系统设计开发中这种模式一般比较少用,而在系统重构或者已有系统维护中会使用一些。当然在新的系统中使用适配器也会存在,主要用于一些相似功能跨平台或多系统支持,比如silverlight的跨平台底层适配器层等。


门面模式

就象字面含义一样,门面模式的作用是为了简化用户对一系列相关对象及操作的使用。比如用户买包子,馒头一样,用户并不需要知道包子,馒头的制造过程和细节,也不需要进入厨房,而只需要在门面,向服务员进行购买,拿包子,找钱,打包的事情都有服务员来完成,用户最后只需要得到要买的东西和找零的钱即可。门面模式将一系列复杂而相关的操作封装成一个简单的操作接口。比如就编译子系统来说,有很多的操作如,词法扫描、语法分析等,这些操作都由不同的对象来负责。
但对于编译系统来说,它不需要知道这些子系统的具体实现细节,它关心的是怎样完成编译操作。这时,我们就可以为编译系统定义一个简单的接口,由这个接口来组合负责相关操作的对象来协同工作,完成编译工作。

门面模式的核心是为了简化操作接口,使系统变得易用。

下面是个例子:

#endregion
/// <summary>
/// Facade 的摘要说明。
/// </summary>
public class Facade
{
  public Facade()
  {
   //
   // TODO: 在此处添加构造函数逻辑
   //
  }
  public  void DoSomething()
  {
   Facade_Opt1 opt1 = new Facade_Opt1();
   Facade_Opt2 opt2 = new Facade_Opt2();
   Facade_Opt3 opt3 = new Facade_Opt3();
   opt1.DoSomething();
   opt2.DoSomething();
   opt3.DoSomething();
  }
}
public class Facade_Opt1
{
  public void DoSomething()
  {
   ;
  }
}
public class Facade_Opt2
{
  public void DoSomething()
  {
   ;
  }
}
public class Facade_Opt3
{
  public void DoSomething()
  {
   ;
  }
}

门面模式没有相对固定的结构模式,但在设计中十分常用,特别是用户交互设计中。用户不需要知道系统内部的处理业务逻辑的细节,而只关心输入和结果是否正确。比如数据库连接,用户只需要填入用户名和密码,然后调用一个接口进行连接,至于数据库连接的过程,如密码加密,解密,所采用的数据库链路之类的,用户一般都不会关心,而由门面服务来完成。

门面模式也为一系列服务对象提供不同的服务提供了一种组合途径。

与其它模式的关系:

1)代理模式:两种模式都起到一种“代理”的作用。代理模式是为一个对象提供一种控制访问该对象的一种机制,并不改变对象的接口。而门面模式是为一系列相关的对象的访问,提供一种简化的访问接口,代理对象需要实现被代理者的接口,而门面模式的门面对象则不需要。

2)代理模式,装饰模式,策略模式,桥模式等都是为了增加或者改变对象的功能或者行为,而且改变的对象都是一类的。而门面模式并不改变对象的接口和功能,只是对外提供一种新的简化了的接口,对内则协调各个对象之间的操作。

3)适配器模式改变了对象的接口,起到了标准兼容的作用,门面模式从外面看,也改变了访问这些对象的方式,但门面模式并不改变某个对象的接口,提供的访问接口只是为了简化对这一系列对象的访问。门面模式提供了一种协同工作的方式。

 

后记:门面模式的根本动力在于,我们在设计中为了共用或者复用,底层的很多业务逻辑(可以看做是零件逻辑)并不单纯只是为了某个特定的应用,为了公用或者复用,显得碎化,再提供给外部使用的时候,就需要根据需要进行组装,为了减少用户对这个组装的依赖,往往提供一系列的组合接口(也就是门面接口)给外部使用。门面模式在实际生活中处处可见,组件化编程其实也源于这种思想。制造业中零件-组件-成品的制造过程也是这种思想的体现,而且提供了很大的灵活性。当然,为了支持这种设计模式,底层的零件逻辑或者是组件逻辑需要分割合理,接口要标准,而且在连接和事务处理方面也要考虑周详。


命令模式

命令模式的意图一是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;二是对请求排队或记录请求日志,以及支持可撤消的操作。简略图如下:

命令模式通过对命令的封装,将命令的请求(调用者Invoker)和执行(接收者Receiver)进行了责任分离,委派给不同的对象,不仅使得调用者和执行者之间实现了解耦(命令的请求方就不需要知道接收方的接口,也不需要知道命令是如何执行的具体情况),还使得可以记录命令的执行记录,添加执行日志,使得命令的控制、执行、取消和重做变得容易。

Delphi的Action就采用了这种模式,其中Windows控件(比如按钮,菜单)是调用者,Action是命令,而接收者是Action的OnExecute事件的实现者。当然,Delphi为了实现设计期间或者运行期间控件的变灰,控件的Caption能在需要时与Action保持一致,在中间加了一个ActionLink类层次结构,采用的是桥模式,控件将控件与Action之间的通信交给ActionLink来完成,而AcionLink对象的创建则采用的是工厂方法(工厂就是控件本身),只不过它利用了GetActionLinkClass函数做得更加巧妙。而Action的具体执行采用事件的方式,又使得命令和接收者进一步解耦,这样命令就不需要知道接收者的接口,也不用维护对接收者的引用,接收者也不需要知道命令的具体细节,只需要提供一个符合要求的事件处理方法即可。

下面是示例代码:

/// <summary>
/// Command类,定义一个执行操作的接口,也可以是一个接口。
/// </summary>
public abstract class Command_Command
{
  protected Command_Receiver _Receiver;
  public abstract void Execute();
  public Command_Receiver Receiver
  {
   get
   {
    return _Receiver;
   }
   set
   {
    _Receiver = value;
   }
  }
}
public class Command_ConcreateCommand : Command_Command
    {
  public Command_ConcreateCommand()
  {
  }
  public override void Execute()
  {
   if(System.Windows.Forms.MessageBox.Show("你想执行操作么?","系统提示!",
    System.Windows.Forms.MessageBoxButtons.YesNo)==System.Windows.Forms.DialogResult.Yes)
   {
    if(this._Receiver!=null)
    {
     this._Receiver.Action();
    }
   }
  }

    }
public class Command_Receiver
{
  public Command_Receiver()
  {
  }
  public void Action()
  {
   System.Windows.Forms.MessageBox.Show("Command Execute!");
  }
}
public class Command_Invoker
{
  private Command_Command command;
  public Command_Invoker()
  {
  }
  public void SetCommand(Command_Command command)
  {
   this.command = command;
  }
  public void Execute()
  {
   this.command.Execute();
  }
}
public class Command_Client
{
  public static void Test()
  {
   //建立具体命令
   Command_ConcreateCommand command = new Command_ConcreateCommand();
   //建立具体接收者
   Command_Receiver r1 = new Command_Receiver();
   //链接命令与接收者
   command.Receiver = r1;
            //创建调用者
   Command_Invoker invoker = new Command_Invoker();
   //设置调用者的命令子类
   invoker.SetCommand(command);
   //执行调用操作
   invoker.Execute();
  }
}

总结:

     如果没有将命令的请求和执行进行责任分离并委托给不同的对象,那么请求与执行都需要在同一个对象内完成,比如Button的Click命令,但这就带来一个问题,Button的Click逻辑太复杂,因为同样是Button,不同的用户请求Click命令时执行的业务逻辑可能完全不同,而且通过Button的子类化来实现也根本不现实。所以,命令的请求和执行的责任必须分离为好,要实现分离有两种办法,一是采用回调函数(原来的Windows系统比较普遍),二是采用事件或委托(Delphi对象方法),三就是采用命令模式。1,2实际上属于同一类型的处理方式,好处是简单(后记:这种应用模式虽然比较普遍,但还有一个缺点就是页面设计者和页面逻辑实现很难分离,比如dotnet的aspx页面和对应的.cs之间的耦合由于太紧密,很难实现UI设计和UI逻辑实现的分离,silverlight的VM就是为了弥补这个缺陷.)。但缺点是没法对命令本身进行管理(执行,取消,日志,重做等)。如果命令本身不需要管理和控制,使用2比较好,现在的界面控件的命令处理基本都是采用这种方式进行。如果需要管理命令本身就要采用Command模式。

     如果对接收者进行抽象,就可以实现命令和接收者的动态组合,这有点类似装饰模式,策略模式。

     对命令模式的一个改进就是可以将命令中接收者引用除掉,利用事件或者委托的办法与接受者发生联系。如果再对调用者进行抽象,这样就形成了一个调用者体系和命令体系,如果需要两者的联系可再增加一个联系层,这样就有3个相对独立的层次体系,Delphi的Action模式就是这样的。这样做有利于系统的可视化设计。

后记:命令模式在现在的编程体系中,使用非常广泛,特别是在UI层与控制层或者业务逻辑层之间的解耦合方面作用非常大,当然,不好的地方是增加了系统的复杂度。silverlight编程中的mvvm模式中的vm层其实就可以看做是一个命令层,由这个层衔接M和V两层,同时这个层与页面层得衔接不再是传统的页面和.cs的关系,而是采用直接在UI中进行绑定的方式进行(传统的方式是将页面事件绑定到对应的后台.cs文件,这个文件在mvvm模式中也有,除了缺省的代码和挂接VM的代码外,已经没什么代码,如果采用动态绑定V-VM,基本就只剩下缺省代码了,因此UI设计完全不用考虑对这个文件的影响),这就使得页面设计和页面实现逻辑分离,可以很好的实现UI设计和UI逻辑开发的责任分割,从而实现设计更加专业化。副作用就是需要增加了很多类和接口。


访问者模式

《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。

设想一个场景,就是学校,学校里有老师和学生,老师和学生可以称为两种元素,我们可以对这些元素进行很多操作(注意,这些操作都是外部性质的,不属于元素本身,这个至关重要),比如评价,问卷调查,采访和体检等,如果我们把这些操作定义在元素层次,显然不合理,一是这些操作不是元素对象的固有属性和行为,而是一些外部操作,不符合面向对象的封装和责任单一原则。如果把这些操作定义在学校这层,显然也不合理,因为学校的责任只是对老师和学生进行管理,这些操作也不是学校的职责。既然不能定义在元素层和学校层,我们可以单独定义一个层次结构来实现,但这里面有两个问题,一是这些操作可能对不同的元素有不同的形式,二是这些操作还必须得到元素的许可,即元素可以接收你的访问。上面的场景其实就是访问者模式的典型应用场景,下面访问者的简图:

上面场景的实现代码:

using System;
using System.Collections.Generic;
using System.Text;

namespace DesignModelStudy
{
    public abstract class Person
    {
        public abstract void Accept(Person_Visitor VA);
        public abstract int GetPersonType();
    }

    //Studeng对象.
    class Student : Person
    {
        private int _id;
        private string _name;
        public override int GetPersonType()
        {
            return 1;
        }
        public Student(int ID, string Name)
        {
            this._name = Name;
            this._id = ID;
        }
        public int ID
        {
            get
            {
                return this._id;
            }
            set
            {
                this._id = value;
            }
        }
        public string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                this._name = value;
            }
        }
        public override void Accept(Person_Visitor VA)
        {
            System.Windows.Forms.MessageBox.Show(VA.Visit(this));
        }
    }
    //Teacher对象.
    class Teacher : Person
    {
        private int _id;
        private string _name;
        private int _age;
        public Teacher(int ID, string Name,int Age)
        {
            this._name = Name;
            this._id = ID;
            this._age = Age;
        }
        public int ID
        {
            get
            {
                return this._id;
            }
            set
            {
                this._id = value;
            }
        }
        public string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                this._name = value;
            }
        }
        public int Age
        {
            get
            {
                return this._age;
            }
            set
            {
                this._age = value;
            }
        }
        public override void Accept(Person_Visitor VA)
        {
            System.Windows.Forms.MessageBox.Show(VA.Visit(this));
        }
        public override int GetPersonType()
        {
            return 2;
        } 
    }
    //人员管理类
    class School
    {
        private System.Collections.ArrayList _students;
        public School()
        {
            _students = new System.Collections.ArrayList();
        }
        public void Add(Person st)
        {
            if (this._students.IndexOf(st) < 0)
            {
                this._students.Add(st);
            }
        }
        public void Del(Person st)
        {
           this._students.Remove(st);
        }
        public Person GetStudent(int index)
        {
            if (index >= 0 && index < this._students.Count)
            {
                return (Person)this._students[index];
            }
            return null;
        }
        public Person this[int index]
        {
            get
            {
                if (index >= 0 && index < this._students.Count)
                {
                    return (Person)this._students[index];
                }
                return null;
            }
        }
        public int Count
        {
            get
            {
                return this._students.Count;
            }
        }
    }

    
    public abstract class Person_Visitor
    {
        public abstract string Visit(Person p);
    }
    //对人员进行评价
    public class EvaluateVisit : Person_Visitor
    {
        public override string Visit(Person p)
        {
            if (p is Student)
            {
                return "学生:" + ((Student)p).Name + " 评价" + " 结果:10";
            }
            else
            {
                return "老师:" + ((Teacher)p).Name + "" + " 年龄:" + ((Teacher)p).Age.ToString() + " 评价结果:30";
            }
            
        }
    }
    //对人员进行采访
    public class InterviewVisit : Person_Visitor
    {
        public override string Visit(Person p)
        {
            if (p is Student)
            {
                return "学生:" + ((Student)p).Name + " 已经采访" + " 结果:10";
            }
            else
            {
                return "老师:" + ((Teacher)p).Name + "已经采访" + " 年龄:" + ((Teacher)p).Age.ToString();
            }

        }
    }
    public class VisitorTest
    {
        private School _pMgr;
        public VisitorTest()
        {
            _pMgr = new School();
        }
        public void  CreateDatas()
        {
            Random rd = new Random();
            
            for (int i = 0; i < 10; i++)
            {
                if ((i % 2) == 1)
                {
                    this._pMgr.Add(new Student(i, "学生" + i.ToString()));
                }
                else
                {
                    this._pMgr.Add(new Teacher(i, "老师" + i.ToString(), 30 + Convert.ToInt32(rd.NextDouble() * 100)));
                }
            }
        }
        public void Test()
        {
            EvaluateVisit ev = new EvaluateVisit();
            for (int i=0;i<this._pMgr.Count;i++)
            {
                 this._pMgr[i].Accept(ev);
            }
        }
    }
}
总结:

1)访问者模式提供了一种将数据结构和附加到该数据结构的操作进行分离解耦的方法,当然,要使用访问者模式,有一个必要条件就是数据元素要比较多而且进行集合式管理。原因非常简单,如果数据结构的元素数目比较少,而且不能进行批处理(集合的好处就是可以进行批处理),利用访问者模式就没有什么优势。

2)访问者模式适合于对那些元素数目多(而且能进行整体性管理,比如集合,组合,树型结构),需要进行附加操作(但操作多,变化大,不确定)处理的地方。

3)访问者模式一般跟集合,组合模式一起使用会比较多。

4)特别注意,访问者模式并不是为了将类的数据属性和操作属性分离。访问者模式提供的是一种数据元素与附加操作之间进行协调管理的一种方式。

 

现在的编程语言中采用这种模式的地方很多,比如对流操作中的stream和reader,writer,针对可枚举对象的迭代和查询等。


观察者模式

在日常生活中,有很多需要我们关注的事务(比如,股市,楼市等),这些事务我们可以称之为主题或者叫信息发布者,观察主题的目的是想了解主题的变化(消息)。一种方法当然是采用盯人策略,但这种方法有个固有的缺点,就是你盯住主题的时候,无法干其他事情,如果需要了解的主题比较多,这种办法就很麻烦了;另外一种就是主题广播,我想听的时候我就去听,不想听的时候我就不听,这种方式的好处就是可以使得观察者不用盯住主题,但缺点是如果信息发布者的信息发布是不固定的,观察者(信息接收者)可能会漏掉信息。这两种方式都各有利弊,局域网中的信息传送采用的其实就是主题广播方式,只是接收者也是盯人战术。还有一种方式就是信息发布者提供一个注册机制,如果你要关注这个主题,就可以登记,如果主题有消息时,就按协定好的方式来通知注册的观察者。这就是观察者模式的现实生活中的原型。

《设计模式》中对观察模式的定义是:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。简图如下:

示例代码如下:

using System;
using System.Collections;

namespace DesignModelStudy
{
#region 观察者模式
/* 名称:Observer 观察者模式
  * 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
  * 适用性:
  * 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。 
     * 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。 
     * 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。 
  * 
  * 结构:
  *      | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                  | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
  *      |   Subject        | observers                        |   Observer       |
  *      |------------------|--------------------------------→|------------------|
  *      | Attach(Observer) |                                  |   Update()       |
  *      | Detach(Observer) | | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|     |__________________|
  *      | Notify()  ○-----|-| for all o in observers{  |             |
  *      |__________________| |     o->Update() }        |             |
  *              △           |__________________________|             | 
  *              |                                                     |
  *              |                                                     |
  *              |                                                     △
  *              |                                                     |              
  *  | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|                                  |    
  *  |    ConcreateSubject          | subject               | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|      
  *  |------------------------------|←---------------------|  ConcreateObserver     |          
  *  |    GetState()  ○------------|-| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄||------------------------|    | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|        
  *  |    SetState()                | |return subjectState ||   Update()  ○---------|----| observerState=subject->GetState()    |                     
  *  |------------------------------| |____________________||------------------------|    |______________________________________|
  *  |    subjectState              |                       |   observerState        |
  *  |______________________________|                       |________________________|
  *
  * 
  * 
  * 
  * */
#endregion
/// <summary>
/// 抽象观察者,定义了一种主题与具体观察者之间进行通信的标准,有两种含义:1,为主题定义了所能接收其注册的观察者的接口标准,
    /// 2定义了主题发布信息时与观察者之间通信的接口方法(观察者提供给主题进行传送消息的方法)。
/// </summary>
public abstract class Observer_Observer
{
       public abstract void Update();
}
    /// <summary>
    /// 抽象主题,主要实现主题注册和通知的机制(即观察者模式的基本机制的实现)
    /// 这样做的好处是只要是继承自抽象主题的子类都可以应用观察者模式。有利于主题的变化和扩展。
    /// </summary>
public class Observer_Subject
{
  private ArrayList observers = new ArrayList(); 
  //注册
  public virtual void Attach(Observer_Observer observer)
  {
   if(observers.IndexOf(observer)<0)
   {
    observers.Add(observer);
   }
  }
  //取消注册
  public virtual void Detach(Observer_Observer observer)
  {
   observers.Remove(observer);
  }
  //通知
  public virtual void Notify()
  {
   foreach( Observer_Observer o in observers)
   {
               o.Update();
   }
  }
}
    /// <summary>
    /// 具体的主题
    /// </summary>
public class Observer_ConcreateSubject : Observer_Subject
{
  private string _Name;
  private double _Value;
  public Observer_ConcreateSubject(string name,double val ) : base()
  {
   this._Name = name;
   this._Value = val;
  }
  public string Name
  {
   get
   {
    return _Name;
   }
   set
   {
    _Name = value;
   }
  }
  public double Value
  {
   get
   {
    return _Value;
   }
   set
   {
    _Value =value;
   }
  }
}
/// <summary>
/// 具体观察者1
/// </summary>
public class Observer_ConcreateObserverA : Observer_Observer
{
        private string _Name;
  private double _Value;
  private Observer_ConcreateSubject subject;
  public override void Update()
  {
   _Name = this.ToString()+"."+subject.Name;
   _Value = subject.Value;
   System.Windows.Forms.MessageBox.Show(_Name+":"+_Value.ToString());
  }
  public Observer_ConcreateObserverA(Observer_ConcreateSubject s)
  {
   this.subject = s;
  }
}
    /// <summary>
    /// 具体观察者2
    /// </summary>
public class Observer_ConcreateObserverB : Observer_Observer
{
  private string _Name;
  private double _Value;
  private Observer_ConcreateSubject subject;
  public override void Update()
  {
   _Name = this.ToString()+"."+subject.Name;
   _Value = subject.Value;
   System.Windows.Forms.MessageBox.Show(_Name+":"+_Value.ToString());
  }
  public Observer_ConcreateObserverB(Observer_ConcreateSubject s)
  {
   this.subject = s;
  }
}
public class Observer_Client
{
  public static void Test()
  {
   Observer_ConcreateSubject subject = new Observer_ConcreateSubject("Item",1000);
            Observer_Observer o1= new Observer_ConcreateObserverA(subject);
   Observer_Observer o2= new Observer_ConcreateObserverB(subject);
   subject.Attach(o1);
   subject.Attach(o2);
   subject.Notify();
  }
}
}

考虑到主题的多样性和观察者的关注点和角度的多样性,上面的观察者模式具有很大的局限性:

1)抽象主题采用抽象类来实现,就会导致一个主题只能接收一种观察者,如果主题本身就是有类层次结构的就要求系统必须支持多继承方式,这显然是个很过分的要求。抽象主题采用接口方式实现可以解决上述问题,这是一种改进;但又造成一个问题,因为从设计来讲,我们希望对象的责任能够比较单一,如果采用接口实现,那么对观察者的注册管理就必须由具体主题类来实现,这显然增加了主题类的责任(而且是额外的),一个解决办法就是将注册管理委托给一个对象类来进行,我们可以称之为注册管理对象,这样一来好处有2:一是使得主题的责任单一,二是解除了主题对观察者的依赖,这时候的多个主题之间不需要有公共的接口。当然,主题需要维护一个对注册类的引用,但即使是这样,好处还是很多,而且可扩展性变得更强;

2)在实际应用中不同的观察者对于关注的主题和侧重点是不一样的,观察者必须知道具提要观察的主题的结构,这就造成了观察者对主题的依赖。这种依赖即使观察者模式的局限,也是观察者模式在很大程度上无法避免的情况。

3)增加注册管理对象后,除了分离了一部分主题的责任,使得主题责任单一,并对观察者解耦外,还有一个好处就是可以在注册对象增加针对观察者的额外功能(比如观察者计数等)。但如果在不需要对观察者进行管理的情况,无论是改进前的还是改进后的观察者模式都有一个不利的地方,就是增加了开销,因为都需要维护一个观察者对象池。改进的办法就是利用事件来实现。利用事件来实现观察者模式有两个好处,一是只要一个事件类型定义,不需要定义接口和实现接口,二是不需要维护观察者对象池。如果将主题要发布的信息参数化,而且观察者除了要知道这个消息,并不需要了解主题的具体细节外,还可以解除观察者与主题之间的耦合关系。

下面是改进后的观察者模式的实现示例:

using System;
using System.Collections;
namespace DesignModelStudy
{

//观察者接口定义
public interface Observer1
{
  //被观察者发生改变时会调用观察者的这个方法。
  void Update(object subject);
}
/// <summary>
/// 观察者聚合集,这样被观察者就知道了有那些观察者在注视它们,以便被观察者发生改变时好通知它们
/// 但这里仅仅是个接口,具体的管理有实现类完成。
/// </summary>
public interface Subject1
{
  //注册观察者
  void AddObserver(Observer1 observer);
  //取消注册
  void RemoveObserver(Observer1 observer);
  //实现该接口以通知所有的观察者.
  void Notify(object realSubject);
}
/// <summary>
/// 具体实现观察者管理的具体对象。 
/// </summary>
public class SubjectHelper : Subject1
{
  private ArrayList observers = new ArrayList();
  public void AddObserver(Observer1 observer)
  {
   observers.Add(observer);
  }
  public void RemoveObserver(Observer1 observer)
  {
   observers.Remove(observer);
  }
  public void Notify(object realSubject)
  {
   foreach(Observer1 observer in observers)
   {
    observer.Update(realSubject);
   }
  }
}
//注意这里的Album不从Subject或SubjectHelper继承,显示了另外一种观察者模式
//这样可以使得对象间的耦合度更小。
//这是被观察者对象
public class Album
{
  private String name;
  //被观察者要知道观察者管理对象,以便通过这个类实现对观察者的管理和通知。
  private Subject1 playSubject = new SubjectHelper();
  public Album(String name)
  { this.name = name; }
  public void Play()
  {
   playSubject.Notify(this);
   // code to play the album
  }
  public String Name
  {
   get { return name; }
  }
  public Subject1 PlaySubject
  {
   get { return playSubject; }
  }
}
    
class Observer1_Client
{
  //普通方式
     public static void Test()
  {
   BillingService billing = new BillingService();
   CounterService counter = new CounterService();
   Album album = new Album("Up");
   album.PlaySubject.AddObserver(billing);
   album.PlaySubject.AddObserver(counter);
   album.Play();
  }
        //利用事件来实现
  public static void Test1()
  {
   BillingService billing = new BillingService();
   CounterService counter = new CounterService();
   Album_1 album = new Album_1("Up");
   album.PlayEvent += new Album_1.PlayHandler(counter.Update);
   album.PlayEvent += new Album_1.PlayHandler(billing.Update);
   album.Play();
  }
}
//具体的观察者类,每个观察者对被观察对象的关注角度是不同的,下面仅仅列出两种观察类
public class CounterService : Observer1
{
  public void Update(object subject)
  {
   if(subject is Album_1)
    GenerateCharge((Album_1)subject);
  }
  private void GenerateCharge(Album_1 album)
  {
   string name = album.Name;
   //code to generate charge for correct album
   System.Windows.Forms.MessageBox.Show("Counter:"+name);
  }
}
public class BillingService : Observer1
{
  public void Update(object subject)
  {
   if(subject is Album_1)
    GenerateCharge((Album_1)subject);
  }
  private void GenerateCharge(Album_1 album)
  {
   string name = album.Name;
   //code to generate charge for correct album
   System.Windows.Forms.MessageBox.Show("Billing:"+name);
  }
}
    //事件方式实现.
    public class Album_1
    {
        private String name;
        public delegate void PlayHandler(object sender);
        public event PlayHandler PlayEvent;
        public Album_1(String name)
        { this.name = name; }
        public void Play()
        {
            Notify();
            // code to play the album
        }
        private void Notify()
        {
            if (PlayEvent != null)
                PlayEvent(this);
        }
        public String Name
        {
            get { return name; }
        }
    }
}

上面的讨论基本都局限于观察者与主题之间的组织,没有讨论观察者和主题之间的通信(上面的模式基本都采用调用通信),实际上主题和观察者之间的通信方式很多,但这与具体的操作系统和网络有关。在通信方式可以支持的情况下,不仅可以实现消除观察者和主题之间的耦合关系,还可以让观察者和主题位于不同的地址空间。

典型应用:MVC模式,Delphi的数据感知控件和数据集,C#的控件和绑定数据源等。


中介者模式

在很多情况下对象之间的交互是很复杂的,在开始设计的时候,你根本不知道它需要交互的对象是谁,是什么类型。我们知道如果要对象A与对象B之间能相互交互,A与B就会构成相互依赖,如果对象一多,这种依赖就会很复杂,况且很多时候我们都不知道需要互相交互的对象有哪些,因此,在设计的时候也没办法考虑这些情况。既然在设计的时候无法预先知道对象之间的交互具体情况,我们可以将这种交互放到一个“中间平台”进行,这个中间平台知道所有对象的(依赖于所有对象,但对象是否必须依赖于平台不是必须的),这样就将对象之间的依赖简化成对象与“平台”之间的依赖,因而会大大降低对象之间的复杂度。当然,对象在平台能够进行交互,必须遵守一定的协议,而且这个平台需要知道所有对象的细节。(这在开发实现时其实算不上什么条件,比如Delphi和C#,Java所有的类都有一个共同的基类,因此不需要额外的为这些交互的对象定义一个抽象的接口之类的)。这种平台起到的作用就是一种中介的作用。

《设计模式》疑问对中介模式的定义:用一个中介对象来封装一系列的对象(同事对象)交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介模式有三个角色:一个是抽象同事,这个角色的定义纯粹是为了使得中介者在定义交互方法的参数时能形式统一,不依赖于具体的同事(一般情况下不需要定义这个抽象类,因为常见的开发语言都有一个基本的类类型(如Delphi,C#的Object类)),二是具体同事类(要交互的对象,可以是已有的,现定义的等),三是中介。下面是中介模式的简图:

示例:

public struct Mediator_Param
{
  public string Name;
  public string Value;  
}
/// <summary>
/// Mediator 的摘要说明。
/// </summary>
public abstract class Mediator_Mediator
{
  //定义中介者的其它共同属性
  //下面的属性一般用事件处理,比较好
  public  abstract void ConcreateColleagueADataChange(Mediator_Colleague sender, Mediator_Param Param);
  public  abstract void ConcreateColleagueBDataChange(Mediator_Colleague sender, Mediator_Param Param);
}
public class Mediator_ConcreateMediator : Mediator_Mediator 
{
  private Mediator_ConcreateColleagueA concreateColleagueA;
  private Mediator_ConcreateColleagueB concreateColleagueB;
  public override void ConcreateColleagueADataChange(Mediator_Colleague sender, Mediator_Param Param)
  {
           if(Param.Name=="tian")
      concreateColleagueB.ShowValue(Param.Value);  
  }
  public override void ConcreateColleagueBDataChange(Mediator_Colleague sender, Mediator_Param Param)
  {
   concreateColleagueB.ShowValue(Param.Name+"  "+Param.Value);  
  }
  public void IntroduceConcreateMediator(Mediator_ConcreateColleagueA concreateColleagueA,Mediator_ConcreateColleagueB concreateColleagueB)
  {
   this.concreateColleagueA = concreateColleagueA;
   this.concreateColleagueB = concreateColleagueB;
  }
  public Mediator_ConcreateMediator()
  {
  }
}
public abstract class  Mediator_Colleague 
{

}
public class Mediator_ConcreateColleagueA : Mediator_Colleague 
{
  private Mediator_Mediator mediator;
  public Mediator_ConcreateColleagueA(Mediator_Mediator mediator)
  {
   this.mediator = mediator;
  }
  public void DataChanged()
  {
   if(mediator!=null)
   {
    Mediator_Param param;
    param.Name = "tian";
    param.Value="121212";
    mediator.ConcreateColleagueADataChange(this,param);
   }
  }
}
public class Mediator_ConcreateColleagueB : Mediator_Colleague
{
  private Mediator_Mediator mediator;
  public Mediator_ConcreateColleagueB(Mediator_Mediator mediator)
  {
   this.mediator = mediator;
  }
  public void DataChanged()
  {
   if(mediator!=null)
   {
    Mediator_Param param;
    param.Name = "zhang";
    param.Value="888888";
    mediator.ConcreateColleagueBDataChange(this,param);
   }
  }
  public void ShowValue(string Value)
  {
   System.Windows.Forms.MessageBox.Show(Value);
  }
}
public class Mediator_Client
{
  public static void Test()
  {
   Mediator_ConcreateMediator mediator = new Mediator_ConcreateMediator(); 
   Mediator_ConcreateColleagueA mcA= new Mediator_ConcreateColleagueA(mediator);
            Mediator_ConcreateColleagueB mcB= new Mediator_ConcreateColleagueB(mediator);
   mediator.IntroduceConcreateMediator(mcA,mcB);
   mcA.DataChanged();
   mcB.DataChanged();
  }
}

中介模式的优点:

减少子类生成;将同事类解耦;简化了对象之间的协议;对对象如何协作进行了抽象;使控制集中化;

中介模式的实现改进:

1)由于一般开发语言都有一个基本父类,因此如果采用这个基本的类作为抽象同事,则在实现时不需要显示定义。

2)采用事件或者消来进行对象之间的交互,则可以使得同事对象不依赖于中介,进一步解耦中介与对象之间的关系。

典型应用场景:

1)现实生活中的各种交互平台(比如交易所,超市,集市,购物广场,交易会,网络论坛等),特别注意,现实中的很多中介公司(比如留学服务中介等)的中介含义还是与中介模式中的中介含义不太一样,它们除了体现一种“中介”外,还有“代理”的味道在里面。中介模式中的中介其实仅提供一个交互的平台,具体的交互还是对象之间来完成的

2)开发系统中的用户UI界面。

比较:

中介者模式与门面模式虽然有类似的功能,但两者之间的区别还是很大的,门面模式时为用户使用一系列的对象提供一个简化的接口,更多体现的是一种“代理”而不是“中介”;而中介者模式主要是为一系列的对象提供一个交互的场所,中介者对象封装了一系列对象之间的交互,中介者模式中没有用户的角色概念,也不向外提供服务。


责任链模式

在一些情况下,对一个消息(含事件)的响应和处理需要很多对象来参与,这些对象对消息的处理有前后顺序,形成一个处理链条,但对象是否真正处理消息有赖于在它之前的对象的处理策略,前一个对象处理后,后一个对象则不需参与处理,这就是责任链模式。现实中有很多类似的场景,比如上访,上访一般是从最基层的信访部门接受信访开始进行处理,如果下一级信访部门无法处理,则交给上一级部门进行处理,这样一级一级的处理,知道最高信访部门。这样所有层级的信访部门就构成了一个责任链。

        责任链模式在设计中的应用很多,比如Delphi中对系统对消息的处理,对异常的处理等。只是建立责任链的方式可能不同,有的是建立明确的处理责任链,如消息处理一般是根据控件的Parent来建立责任链条;有的则是暗含一种处理机制,比如Delphi的异常处理(C#也类似)总是从最内层到外,从被调用对象到调用对象这条链进行。下面是责任链模式的一种简单示例:

public abstract class Chain_Successor
{
  protected Chain_Successor successor; //保存后继责任者的引用
  /// <summary>
  /// 处理需求
  /// </summary>
  /// <param name="request">需要处理的类</param>
  public abstract void HandleReqest(Chain_Request request);
  /// <summary>
  /// 设置后继责任者
  /// </summary>
  /// <param name="successor">后继责任者</param>
  public void SetChain_Successor(Chain_Successor successor)
  {
   this.successor = successor;
  }
}
public class Chain_ConcreateHandleA : Chain_Successor
{
  public Chain_ConcreateHandleA()
  {
  }
  public override void HandleReqest(Chain_Request request)
  {
   //如果自己能处理则处理,否则传递给后继者
   if(request.Request_Type==1)
   {
    System.Windows.Forms.MessageBox.Show(this.ToString()+":"+request.Request_Parameters);
   }
   else
   {
    if(this.successor!=null)
                   successor.HandleReqest(request);
   }
  }

}
public class Chain_ConcreateHandleB : Chain_Successor
{
  public Chain_ConcreateHandleB()
  {
  }
  public override void HandleReqest(Chain_Request request)
  {
   //如果自己能处理则处理,否则传递给后继者
   if(request.Request_Type==2)
   {
    System.Windows.Forms.MessageBox.Show(this.ToString()+":"+request.Request_Parameters);
   }
   else
   {
    if(this.successor!=null)
     successor.HandleReqest(request);
   }
  }

}
public class Chain_ConcreateHandleC : Chain_Successor
{
  public Chain_ConcreateHandleC()
  {
  }
  public override void HandleReqest(Chain_Request request)
  {
   //如果自己能处理则处理,否则传递给后继者
   if(request.Request_Type==3)
   {
    System.Windows.Forms.MessageBox.Show(this.ToString()+":"+request.Request_Parameters);
   }
   else
   {
    if(this.successor!=null)
     successor.HandleReqest(request);
   }
  }

}
public class Chain_Request
{
  //下面的两个参数可更加具体需要进行构造
        private int _Request_type;
  private string _Request_parameters;
  public Chain_Request(int Request_type,string RequestParameters)
  {
   this._Request_type = Request_type;
   this._Request_parameters = RequestParameters;
  }
  public int Request_Type
  {
   get
   {
    return _Request_type;
   }
   set
   {
                _Request_type = value;
   }  
  }
  public string Request_Parameters
  {
   get
   {
    return _Request_parameters;
   }
   set
   {
               _Request_parameters = value;
   }
  }
}
public class Chain_Client
{
  public static void Test()
  {
            Chain_Successor firstHandler = new Chain_ConcreateHandleA();
   Chain_Successor secondHandler = new Chain_ConcreateHandleB();
   Chain_Successor ThirdHandler = new Chain_ConcreateHandleC();
   firstHandler.SetChain_Successor(secondHandler);
   secondHandler.SetChain_Successor(ThirdHandler);
            firstHandler.HandleReqest(new Chain_Request(2,"hello,the world!"));
  }
}


0 0
原创粉丝点击