状态模式允许一个对象根据当前对象的值来决定其行为。如下图“状态模式示例”是一个关于电灯泡操作的例子。如果电灯的状态为关,当按下开关时状态将打开;如果开关为开,则按下开关灯关闭。简而言之,根据不同的状态行为会发生变化。
图1:状态模式示例
下 面我们尝试使用C#实现该示例。图“状态模式实践”展示了业务类和客户端代码。我们创建了一个类‘clsState’,该类包含有两个状态“On”和 “Off”枚举;还定义了‘PressSwitch’方法,它根据当前状态来切换状态。在图右半部分我们实现了使用类‘clsState’的客户端来调用 ‘PressSwitch方法,并使用‘getStatus’方法将当前状态显示在文本框中。
当我们点击后会来回切换当前状态。
图2:状态模式实践
策略模式在类中封装了算法,可以根据所使用的类来互换。当我们想在运行时决定使用哪个算法时这种模式非常有用。
下 面来看一个策略模式工作原理的例子。我们以数学运算中的加、减为例,图“策略模式实践”展示了该逻辑。它接收连个数字作为参数,根据不同的策略给出不同的 结果。因此,如果使用的是加法策略,那么两个数字会被相加;如果使用的是减法策略,那么两个数字将被相减。这些策略仅仅是算法。策略模式也仅仅是对算法的 封装。
图3:策略模式实践
首 先我们需要知道的是这些算法是如何被封装到类中的。下图“算法封装”展示了“add”算法是如何被封装到‘clsAddStatergy’类、 ‘substract’算法是如何被封装到‘clsSubstractStatergy’类中的;而这些类都继承自‘clsStratergy’类,该类 为子类提供了一个‘calculate’方法。
图4:算法封装
接着定义包装类‘clsMaths’来引用‘clsStatergy’类,并通过‘setStatergy’来设置需要使用的策略类。
图5:策略类及其包装类
下图“策略模式调用代码”展示了包装类的用法以及在运行时通过‘setStatergy’方法设置策略。
图6: 策略模式调用代码
访问者模式允许在不改变实际类的情况下修改类结构。它是一种将逻辑和算法从当前出具结构中分离开来的方式。基于这种方式我们可以在不修改结构的情况下来为当前数据增加新的逻辑;另一方面,我们可以在不接触逻辑的情况下修改数据结构。
下 图“逻辑和数据结构”包含了一个消费者数据结构,每个消费者对象都有多个地址对象,每个地址对象又有多个电话对象。这个数据结构需要以两种不同的格式展 示:String、XML。因此我们需要写两个类其中一个是String相关逻辑的类,另一个是XML逻辑相关的类。这两个类遍历对象结构并给出相应的输 出。简而言之,访问者包含了逻辑。
图7: 逻辑和数据结构
接着我们用C#来实现上面例子。其他编程语言同理。我们创建了连个访问者类来分别解析string逻辑和xml。这两个类都有一个visit方法来解析相应对象。为了保持统一这些类都实现了一个‘IVisitor’通用接口。
图8:访问者类
之前定义的访问者类将被传递给数据结构类,如消费者类。所以,在消费者类中,我们在‘Accept’函数中传入访问者类。在该方法中我们通过传递这种访问者类的类型来调用visit方法;同时visit方法做了重载,因此它将根据所传递的参数类的类型来决定调用哪个方法。
图9:传递访问者类到数据结构
由 于每个消费者都有多个地址,每个地址都有多个电话对象,因此在‘clsCustomer’类中集成有‘objAddresses’集合,而在 ‘clsAddress’类中则集成有‘objPhones’集合。每个对象都有accept方法并持有访问者类对象并在访问者类中的visit方法中解 析自己。由于visit方法的经过重载,所以它将多态的调用适当的方法。
图10:消费者、地址、电话对象
现 在我们定义好了访问者类的逻辑和消费者类的内部数据结构,接着可以在客户端使用它们了。下图“访问者模式客户端代码”展示了使用访问者模式的代码快照。首 先创建访问者对象并将其传递给消费者数据类;如果我们想要以字符串形式展示消费者对象数据结构,那么就创建‘clsVisitorString’,如果想 以xml格式展示,则需创建‘clsXML’对象并将其传递到消费者对象数据结构中。从数据结构中我们可以很容易明白该逻辑。
图11:访问者模式客户端代码
访 问者模式和策略模式看起来非常相似,因为它们都是从数据中封装负责逻辑。可以说访问者模式是策略模式的通用形式。在策略模式中,在多个算法之上有一个上下 文或者单一的逻辑数据对象。在前一个问题中我们解释了策略模式和访问者模式的基本原理,那么让我们再次看下之前已经理解的例子。在策略模式中有一个单一的 上下文类和多个算法。图“策略模式”展示该上下文类和多个算法之间是如何协调工作的。
图12:策略模式
访问者模式有多个上下文对象,每个上下文对象都包含有一个算法。在访问者模式的例子中我们写过解析数据的例子如消费者、地址、电话对象。
图13:访问者模式
因 此,简而言之策略模式是访问者模式的特殊形式。在策略模式中,所有的算法只有一个上下文而访问者模式则每个算法对象都有一个上下文。所以对于选择使用策略 模式还是访问者模式的基本标准就是上下文和算法之间的关系。如果需要一个上下文、多个算法,那么就选择策略模式;如果有多个上下文和算法就选择使用访问者 模式。
很 多时候两个类由于继承自不兼容的接口而变得不兼容。适配器模式帮助我们来包装已存在的类使得两个类变得互相兼容。下图“不兼容接口”中两个集合都持有一个 string值;它们都有一个方法来增加一个字符串到集合中;其中一个方法是‘Add’另一个是‘Push’;一个使用的集合对象而另一个使用的栈。我们 想要使栈对象和集合对象兼容。
图14:不兼容接口
有两种方法实现适配器模式,一种是聚合(这种方式称为对象适配器模式),另一种是继承(这种方式称为类适配器模式)。首先我们来看看对象适配器模式。
下图“对象适配器模式”展示了一个广泛的实现方式。该例子中我们引入了一个新的包装类‘clsCollectionAdapter’,该类包装了‘clsStack’类并且将push方法包含在add方法之中,这样使得两个类变得兼容。
图15:对象适配器模式
另一种方式是使用继承来实现适配器模式,也叫类适配器模式。图“类适配器模式”展示了类‘clsStack’如何通过继承‘clsCollectionAdapter’使得兼容‘clsCollection’类。
图16:类适配模式
当 我们需要创建很多对象并且这些对象都共用一种数据时享元模式非常有用。下图“对象和共用数据”中,我们需要为组织中所有员工打印名片,这样我们有两部分数 据一种是可变数据如员工名称,另一个部分是静态数据如地址。通过只保留一份静态数据并使所有可变对象引用该地址这种方式,可以使内存使用最小化。因此我们 创建不同的可变数据但是都引用同一份静态数据,这样可以优化对内存的使用。
图17:对象和通用数据
以 下是享元模式的一个C#代码实例。其中有两个类:‘clsVariableAddress’来保存动态数据,‘clsAddress’来保存静态数据;为 了确保‘clsAddress’类只有一个实例,我们创建了一个包装类‘clsStatic’以及‘clsAddress’类的静态实例。这个类聚合了 ‘clsVariableAddress’类。
图18:享元模式类图
下图“享元模式客户端代码”中我们创建了‘clsVariableAddress’类的两个对象,但是内部的静态数据如地址类则只有一个实例。
图19:享元模式客户端代码
1. 本文由程序员学架构翻译
2. 本文译自 http://www.codeproject.com/Articles/28384/Design-pattern-FAQ-Part-Design-pattern-training
3. 转载请务必注明本文出自:程序员学架构(微信号: archleaner )
4. 更多文章请扫码: