代码改变世界

设计模式学习(五):行为型模式

2010-12-14 13:04  zhenjing  阅读(2490)  评论(0编辑  收藏  举报

  行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。这些模式刻画了在运行时难以跟踪的复杂的控制流。它们将你的注意力从控制流转移到对象间的联系方式上。

  行为型模式的三个典型特点:

封装变化

对象作为参数

对发送者和接收者解耦

Chain of Responsibility

意图:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。

动机: 解除请求的发送者和接收者之间耦合,在对象间传递待处理的请求。

适用性:用于请求的跨层传递,解除请求发送者和请求的最终处理者之间的耦合。统一地处理客户请求。常用于窗口系统,处理鼠标或键盘事件,典型应用:wxWidget的事件系统。

结构: 客户请求一般采用Command封装,使易于传递。一般不同对象采用统一的接口来处理请求。如果请求处理者对象存放在列表中,一般要求使用继承实现。

优点: 易于请求的跨层传递;解除对象耦合(封装请求的真实处理者);统一请求处理(封装请求的变化)。

组合模式: 经常采用Command来封装不同类型的请求.

Command

意图:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化(传递请求);对请求排队或记录请求日志,以及支持可取消的操作。

动机: 封装请求的类型;让请求具有对象的特性(具有状态的实体),这样就可以传递、保存或者采用不同的方式来处理请求对象。

结构: 用继承来封装请求的类型;除了不一样的构造和初始化函数外,提供一致的核心接口。

优点: 让请求具有对象的特性,使客户能够采用不同的方式来处理请求对象,比如可以解除对象构造和对象使用的耦合,即实体解耦和时间解耦。

用途: 经常用于数据库事务操作,设备控制,多线程核心(Active Object)以及GUI的do/undo管理等。或者用于消除过多的条件分派。

组合模式: 经常用于Chain ofResponsibility中的请求封装;经常和Composite组合使用,提供统一的对待Command的途径,封装“一对多”的关系。

Iterator

意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

动机: 封装对对象的访问规则或者算法。本质:提供一致的遍历接口

适用性: 适用于为多个不同聚合类提供一致的遍历接口。

结构:抽象迭代器提供类似hasNext(),Next(), Remove()等接口,并由具体聚合类通过createIterator()创建具体的迭代器。

优点: 封装聚合类内部实现,提供一致对外接口

Interpreter

意图:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用自身定义表示来解释语言中的句子。

动机: 采用不同的对象来表示不同的文法,使文法易于组合使用。提供一种可选择的方式,将易变的组合逻辑推给客户代码。

结构:将每一个语法规则表示成一个类,方便于实现语言。

适用性:方便实现简单语言的解释器(常用yacc和lex工具来编写语言的解释器原型)

Mediator

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

动机: 封装多个对象间的交互关系,解除耦合。

适用性: 适用于多个对象交互关系复杂且易变的情况。常用于协调GUI组件。

结构: 提供一个幕后类来统一管理不同对象间的交互关系。

优点: 封装多个对象间的交互关系,使客户更加容易编程。

Memento

意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。

动机: 保存对象状态,用于对象状态的回滚或重新构造。

适用性: 用于对象内部状态易变,且对象状态具有某种价值的场合。

结构: 使用简单的结构体即可完成任务。

优点: 提供保存对象的另一种选择。

缺点:存储耗时,常用语言自带的序列化(serialization)机制存储系统状态。

Observer

意图:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。

动机: 封装对象间一对多的依赖关系,提供统一的管理点。

适用性: 所有具有一对多依赖关系且需要传递状态信息的对象管理。

结构: 使用双抽象结构(主题和观察者),一个抽象管理主题状态的通知行为(简化主题派生类的行为),一个抽象用于封装不同的状态观察者。

优点: 在具有多个观察者时,可简化状态通知部分的Hard-coding,并且易于扩展。

变化: 在状态比较复杂的情况下,一般采用某种约定的参数来提示观察者状态发生了什么样的变化,简化对象更新过程。

State

意图:允许一个对象在其内部状态改变时改变它的行为(外部行为)。对象看起来似乎修改了它所属的类。

动机: 分离状态机的逻辑和动作;或者是分离状态和行为(动作)。

结构:state定义所用具体状态的共同接口(事件接口);任何具体状态实现该相同接口。Context拥有所有状态对象。根据不同事件,context在不同状态对象中切换,从而改变自身行为。

优点:避免用户直接和状态交互;去除掉大量的条件语句;使系统更加易于扩展和维护。

变化:1)一般来讲,当状态改变是固定的,状态转换逻辑适合放在Context中;当转换更动态的时候,通常将状态转换逻辑放在State中,但这会使状态类之间产生依赖。总之,该决策决定了究竟哪个类是对修改封闭的(context或state)。2)如有多个Context,则可考虑共享所有的状态类。

  补充:有限状态自动机(FSM)

  有限状态自动机的两种表示方式: 状态迁移图(STD)和状态迁移表(STT)

  状态迁移图(STD)至少由4部分组成。圆形表示状态;连接状态的箭头被称为迁移;迁移被用一个后面跟着动作名的时间做了标记,组成事件/动作对。类似数字电子中的状态转移图,本质上就是一个东西。

  状态迁移表(STT)用一个表的形式来描述系统中状态的转移。表中的每一列表示一个状态迁移的完整过程。由下面4个部分组成一列:

  起始状态  触发迁移的事件  终止状态   所执行的动作

  使用状态迁移图(STD)和状态迁移表(STT)来描述自动状态机是非常有效的,并且非常容易检测那么未知的以及没有处理的状态转移情况。这对编程是很有帮助的,因为在实际编码中,非常容易遗漏非正常的状态转移,而这些遗漏往往是错误的根源。

  实现有限状态自动机(FSM)的技术:

  嵌套的switch/case语句,解释迁移表和State模式。

  在简单其状态迁移中,使用嵌套的switch/case语句就足够的。在复杂情况下,使用State模式比较好。解释迁移表也容易实现,但不是这里讨论的目标,最主要的就是表的查找。

  State的标准结构图:

  State模式彻底的分离了状态机的逻辑和动作。动作是在Context类中实现的,而逻辑则是分布在State类的派生类中。这使得二者可以非常容易的独立变化,互补影响。例如,只要使用State的另一个派生类。就可以非常容易地在一个不同的状态逻辑中重用Context类的动作。此外,我们也可以在不影响State派生类逻辑的情况下创建Context的派生类来更改或者替换动作的实现。

  可以使用状态机的地方:作为GUI中高层应用策略;GUI交互控制器;分布式处理等。几乎凡有状态存在的地方均可以考虑采用有限状态机。

Strategy

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。该模式使得算法的变化可独立于使用它的客户。

动机: 封装不同的算法。定制一组可以互换的算法族。

适用性: 同一个问题存在多种不同的解决方案。

结构: 为了满足算法之间的互换性,必须使用继承,并且遵循Liskov原则。

优点: 封装算法的变化。

组合模式: 经常在使用算法的基类Context中使用Template Method。如果Context的派生类中要求所使用的算法动态改变,还常常把Factory Method内嵌到派生类中来创建不同的算法类。

区别:策略模式和状态模式具有相同类图。策略模式是围绕可互换的算法来创建业务的,由Client自行决定具体策略。状态模式则通过改变对象内部状态帮助对象控制自己的行为。

Template Method

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

动机: 消除子类中的重复代码,简化子类代码。

适用性: 当各个子类中,混杂着不变和可变的行为时,就可以使用该模式。将不变的行为放入父类中,子类只需定制可变的行为。这里不变还包括行为的执行顺序。

结构: 必须使用继承关系。

优点: 消除子类的重复行为。

组合模式: 经常和FactoryMethod、Strategy一起使用。在分解不变和可变行为时,还常可借助组合方法(ComposeMethod)和Collecting Parameter模式。

Visitor

意图:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

动机: 在不改变原类(一般是特定的数据结构)的情况下,增加新的功能接口。

适用性: 原有的类结构不能或者不容易改变;或者新加的功能不属于原类的职责范畴。

结构: 双重分派。增加一个Visitor类,并针对所有要访问的派生类增加单独的访问接口(在Visitor中)。本质上,Visitor模式中的两次分派形成一个功能矩阵。Visitor的接口名字和其接受的派生类类型分别是功能矩阵的两个变化轴。

优点: 使用Visitor模式,使程序中的数据结构(原类)独立于它的用途。

用途: 一般如果应用程序中存在有需要以多种不同方式进行解释的数据结构,就可以使用Visitor模式。比如使用Visitor模式来遍历所有的配置数据来初始化不同的应用程序子系统。最常见的应用:遍历大量的数据结构并产生不同类型的报表。

缺点:会破环组合类的封装。

Null Object

意图: 提供一个没有任何行为的对象。

动机: 消除代码中四处存在的无效对象判断

适用性: 只要对无效对象的判断逻辑多次出现时,就有引入Null Object的必要。

结构: Null Object肯定是作为派生类的一个种类出现,并用于取代没有合适派生类可用的情形。比如,对象放在Map中,查找可能无效。

优点: 消除对无效对象的判断逻辑,提供系统的可靠性。

---------------------------------------------------

兄弟的公司:立即购--手机购物,诚信网购

兄弟的公司:立即团

欢迎转载,请注明作者和出处