设计模式(四)

行为型模式

用于描述类或对象怎样交互以及怎样分配职责,主要特点就是处理的是对象之间的通信方式,控制类与类之间的通讯与相互控制,解决类之间的复杂的交互项操作。

1、责任链模式

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。一个请求沿着一条链在传递,这条链就称为职责链,职责链可以是一条直线、一个环或者一个树形结构。最简单的责任链模式应该就是if-else语句了。
抽象处理者类定义了对下家的引用对象,以便将请求转发给下家,具体处理者是抽象处理者的子类,用于处理请求或者转发请求。职责链模式并不创建职责链,职责链的创建工作必须由系统的其他部分来完成,一般是在使用该职责链的客户端中创建职责链,职责链模式降低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。
在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。增加一个新的具体请求处理者只需要在客户端重新建链,不用修改源代码。
纯的职责链模式:要么承担全部责任,要么将责任推给下家,不允许对一个请求只处理一半,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。
不纯的职责链模式:允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。


适用场景:有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的,可以改变链中处理者之间的先后次序。

 

2、命令模式

将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。
命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使得相同的发送者对应不同的接收者。可以通过对命令集合的下标移动操作来实现撤销和恢复,或者将命令对象通过序列化写到日志文件中来实现恢复。
宏命令又称为组合命令,它是组合模式和命令模式联用的产物。通常宏命令不直接与请求接收者交互,而是通过它的成员来调用接收者的方法。执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理。它拥有一个集合属性,在该集合中包含了对其他命令对象的引用。
降低系统的耦合度,新的命令可以很容易地加入到系统中,可以比较容易地设计一个命令队列或宏命令,为请求的撤销和恢复操作提供了一种设计和实现方案。但是使用命令模式可能会导致某些系统有过多的具体命令类。


适用场景:系统需要将请求调用者和请求接收者解耦,系统需要在不同的时间指定请求、将请求排队和执行请求。


3、中介者模式

用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
提出用组合而不用继承,但类与类之间互相交互就会有各种引用引入,系统结构复杂且耦合度高,每一个界面组件都与多个其他组件之间产生相互关联和调用,若一个界面组件对象发生变化,需要跟踪与之有关联的其他所有组件并进行处理,系统组件之间呈现一种较为复杂的网状结构,组件之间的耦合度高。组件的可重用性差,由于每一个组件和其他组件之间都具有很强的关联,这些组件表现出来更像一个不可分割的整体,而有时我们往往需要每一个组件都能够单独重用,而不是重用一个由多个组件组成的复杂结构。
系统的可扩展性差,如果在上述系统中增加一个新的组件类,则必须修改与之交互的其他组件类的源代码。引入一个“中介者”来降低现有系统中类之间的耦合度,来封装并协调原有组件两两之间复杂的引用关系,使之成为一个松耦合的系统,使用中介者类来协调多个类/对象之间的交互。
中介者模式可以使对象之间的关系数量急剧减少,可以将系统的网状结构变成以中介者为中心的星形结构,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系。


适用场景:系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。中介者将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成新的中介者子类,对于给原先中介者添加交互对象可以继承原先中介者,而不需继承对象覆盖父类方法,减少子类产生。

与外观者区别:都是不用去考虑内部怎么去使用对象,对象之间的交互都由"第三者"来完成,外观者模式内部就是简单去调用与该对象交互的对象方法,而中介者模式可以在调用其他对象交互的时候可以加上自己的逻辑代码。


4、观察者模式

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。观察者模式描述了如何建立对象与对象之间的依赖关系,以及如何构造满足这种需求的系统。
基于观察者模式的委派事件模型,组件所引发的事件并不由引发事件的对象自己来负责处理,而是委派给独立的事件处理对象负责。事件的发布者称为事件源对象也就是目标对象,事件监听器为观察者对象,通过事件对象来传递与事件相关的信息。通常使用的是一对一的观察者模式。
观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察者和观察目标之间不能存在循环依赖否则会循环调用。


适用场景:一个对象的改变将导致一个或多个其他对象也发生改变,需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象。

与中介者模式区别:都是减少对象之间的依赖,使对象之间成为一对多依赖关系。观察者模式强调的是其中一个对象变化的时候需要把变化的东西通知给其他对象,而中介者模式当想通知其他对象的时候就可以去调用方法通知给其他对象。中介者主要是简单的把信息通知给其他对象,而观察者模式是把对象有其他变化的信息通知给其它对象。

 

5、策略模式

定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化。每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开。
用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。策略模式提供了管理相关的算法族的办法,可以避免多重条件选择语句。


适用场景:一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。


6、模板方法模式

定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
是一种基于继承的代码复用技术,其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。
钩子方法:用来判断条件是否满足来执行下一步骤,或者是实现体为空的具体方法。在模板方法模式中,由于面向对象的多态性,子类对象在运行时将覆盖父类对象,子类中定义的方法也将覆盖父类中定义的方法,因此程序在运行时,具体子类的基本方法将覆盖父类中定义的基本方法,子类的钩子方法也将覆盖父类的钩子方法,从而可以通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制。
它提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为。在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。


适用场景:对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。


7、访问者模式

提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
有时候我们需要处理集合对象结构,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式,还有可能增加新的处理方式。
对象结构是一个元素的集合,存储了不同类型的元素对象,以供不同访问者访问,可以是List集合(定义一个父类集合,可以把子类添加进去,并不需要去判断强转成具体子类,只要通过父类调用方法就行),或者是组合模式实现的集合结构。如果要在系统中增加一种新的访问者,无须修改源代码,只要增加一个新的具体访问者类即可。如果要在系统中增加一种新的具体元素,需要对原有系统进行修改,在原有的抽象访问者类和具体访问者类中增加相应的访问方法。
双重分派机制:在父类中定义方法来统一作判断,子类把自己作为参数传给父类实现具体的逻辑代码,这样每一个子类都是重用代码而不用每个覆写方法。


适用场景:一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。

 

8、解释器模式

定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。在某些情况下,为了更好地描述某一些特定类型的问题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问题的实例将对应为该语言中的句子。

环境类Context用于存储解释器之外的一些全局信息,向表达式解释器提供一些全局的、公共的数据。增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。


适用场景:可以将一个需要解释执行的语言中的句子表示为一个抽象语法树,一些重复出现的问题可以用一种简单的语言来进行表达。

 

9、迭代器模式

提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素。通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据由迭代器来完成。将聚合类中负责遍历数据的方法提取出来,封装到专门的类中,实现数据存储和数据遍历分离,无须暴露聚合类的内部属性即可对其进行操作。
使用抽象类来设计抽象迭代器,在抽象类中为每一个方法提供一个空的默认实现。如果需要在具体迭代器中为聚合对象增加全新的遍历操作,则必须修改抽象迭代器和具体迭代器的源代码,这将违反“开闭原则”,因此在设计时要考虑全面,避免之后修改接口,为了能够让迭代器可以访问到聚合对象中的数据,我们还可以将迭代器类设计为聚合类的内部类。客户端无须关心具体迭代器对象的创建细节,通过工厂来封装对象的创建过程,简化了客户端的调用。


适用场景:访问一个聚合对象的内容而无须暴露它的内部表示。需要为一个聚合对象提供多种遍历方式。

 

10、备忘录模式

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。
备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
原发器:一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
备忘录:存储原发器的内部状态,备忘录对象不能直接供其他类使用,一般和原发器类一样,或者和原发器部分方法一样,可以考虑用原型模型创建备忘录。
负责人:可以存储一个或多个备忘录对象,不能去修改备忘录对象。


适用场景:保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。

 

11、状态模式

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。
环境类:它是拥有多种状态的对象。抽象状态类:用于定义一个接口以封装与环境类的一个特定状态相关的行为。具体状态类:每一个子类实现一个与环境类的一个状态相关的行为。
环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。统一由环境类来负责状态之间的转换,通过对某些属性值的判断实现状态转换。或者由具体状态类来负责状态之间的转换,
可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换。如果状态的转换条件因不同状态而不同,那么应该在状态具体实现类中做状态转换,但会对其他的状态有依赖;如果状态变化标准对每种状态都是一样的,在环境类中作统一的判断说明,但添加新状态需修改源代码。可以把环境类判断状态的部分可以用状态管理器类来专门判断实现类型转换。
在有些情况下,多个环境对象可能需要共享同一个状态,如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象。
将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为,允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块。


适用场景:对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值