行为型模式
一、行为型模式简介
- 迭代器模式(Iterator)
- 访问者模式(Visitor)
- 命令模式(Command)
- 解释器模式(Interpreter)
- 职责链模式(Chain of Responsibility)
- 备忘录模式(Memento)
- 中介者模式(Mediator)
- 观察者模式(Observer)
- 状态模式(State)
- 策略模式(Strategy)
- 模板方法模式(Template Method)
1. 迭代器模式
1.1 模式动机
(1)一个聚合对象,如一个列表(List)或者一个集合(Set),应该提供一种方法来让别人可以访问它的元素,而又不需要暴露它的内部结构。
(2)针对不同的需要,可能还要以不同的方式遍历整个聚合对象,但是我们并不希望在聚合对象的抽象层接口中充斥着各种不同遍历的操作。
(3)怎样遍历一个聚合对象,又不需要了解聚合对象的内部结构,还能够提供多种不同的遍历方式,这就是迭代器模式所要解决的问题。
(4)在迭代器模式中,提供一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。
(5) 有了迭代器模式,我们会发现对一个复杂的聚合对象的操作会变得如此简单。
1.2 模式定义
迭代器模式(Iterator Pattern) :提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。
1.3 模式结构
迭代器模式包含如下角色:
- Iterator: 抽象迭代器 定义了访问和遍历元素的接口
- ConcreteIterator: 具体迭代器
实现了抽象迭代器接口,完成对聚合对象的遍历。
1.4 模式分析
- 聚合是一个管理和组织数据对象的数据结构。
- 聚合对象主要拥有两个职责:一是存储内部数据;二是遍历内部数据。
- 存储数据是聚合对象最基本的职责。
- 将遍历聚合对象中数据的行为提取出来,封装到一个迭代器中,通过专门的迭代器来遍历聚合对象的内部数据,这就是迭代器模式的本质。迭代器模式是“单一职责原则”的完美体现。
-
在迭代器模式中应用了工厂方法模式,聚合类充当工厂类,而迭代器充当产品类,由于定义了抽象层,系统的扩展性很好,在客户端可以针对抽象聚合类和抽象迭代器进行编程。
-
由于很多编程语言的类库都已经实现了迭代器模式,因此在实际使用中我们很少自定义迭代器,只需要直接使用Java、C#等语言中已定义好的迭代器即可,迭代器已经成为我们操作聚合对象的基本工具之一。
1.5 模式优缺点
1.5.1 迭代器模式的优点 :
- 它支持以不同的方式遍历一个聚合对象。
- 迭代器简化了聚合类。
- 在同一个聚合上可以有多个遍历。
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。
1.5.2 迭代器模式的缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
1.6 模式适用环境
在以下情况下可以使用迭代器模式:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 需要为聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口。
1.7 模式应用
- JDK 1.2 引入了新的Java聚合框架Collections。
-
Collection是所有Java聚合类的根接口。
-
在JDK类库中,Collection的iterator()方法返回一个java.util.Iterator类型的对象,而其子接口java.util.List的listIterator()方法返回一个java.util.ListIterator类型的对象,ListIterator是Iterator的子类。 它们构成了Java语言对迭代器模式的支持,Java语言的java.util.Iterator接口就是迭代器模式的应用。
1.8 模式扩展
Java迭代器
在JDK中,Iterator接口具有如下3个基本方法:
(1) Object next():通过反复调用next()方法可以逐个访问聚合中的元素。
(2) boolean hasNext():hasNext()方法用于判断聚合对象中是否还存在下一个元素,为了不抛出异常,必须在调用next()之前先调用hasNext()。如果迭代对象仍然拥有可供访问的元素,那么hasNext()返回true。
(3) void remove():用于删除上次调用next()时所返回的元素。
1.9 模式案例
电视机遥控器:有一个地方频道集合,另一个是央视频道,将不同类型的频道,进行统一管理,能实现所有频道的前后选择。
2. 访问者模式
2.1 模式动机
对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,
而且不同的访问者其访问方式有所不同,访问者模式为解决这类问题而诞生。 如:医院的划价人员和药房的工作人员
2.2 模式动机
在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式。
而且这些操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求。
此时, 访问者模式就是一个值得考虑的解决方案。
访问者模式的目的(即动机):
- 封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。
- 为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式。
2.3 模式定义
访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
2.4 模式结构
访问者模式包含如下角色:
- Vistor: 抽象访问者
- ConcreteVisitor:具体访问者
- Element: 抽象元素
- ConcreteElement: 具体元素
- ObjectStructure: 对象结构
2.5 模式分析
- 访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。
- 访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。
- 相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。
- 在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。
2.6 模式实例
奖励审批系统 某高校奖励审批系统可以实现教师奖励和学生奖励的审批(AwardCheck),如果教师发表论文数超过10篇或者学生论文超过2篇可以评选科研奖,
如果教师教学反馈分大于等于90分或者学生平均成绩大于等于90分可以评选成绩优秀奖,使用访问者模式设计该系统,以判断候选人集合中的教师或学生是否符合某种获奖要求。
2.7 模式优缺点
访问者模式的优点
- 使得增加新的访问操作变得很容易。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。
- 可以跨过类的等级结构访问属于不同的等级结构的元素类。
- 让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。
访问者模式的缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
2.8 模式适用环境
在以下情况下可以使用访问者模式:
- 一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
- 访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2.9 模式应用
(1) 在一些编译器的设计中运用了访问者模式,程序代码是被访问的对象,它包括变量定义、变量赋值、逻辑运算、算术运算等语句,编译器需要对代码进行分析,如检查变量是否定义、变量是否赋值、算术运算是否合法等,可以将不同的操作封装在不同的类中,如检查变量定义的类、检查变量赋值的类、检查算术运算是否合法的类,这些类就是具体访问者,可以访问程序代码中不同类型的语句。在编译过程中除了代码分析外,还包含代码优化、空间分配和代码生成等部 分,也可以将每一个不同编译阶段的操作封装到了跟该阶段有关的一个访问者类中。
2.10 模式扩展
- 与其他模式联用 由于访问者模式需要对对象结构进行操作,而对象结构本身是一个元素对象的集合,因此访问者模式经常需要与迭代器模式联用,在对象结构中使用迭代器来遍历元素对象。
- 在访问者模式中,元素对象可能存在容器对象和叶子对象,因此可以结合组合模式来进行设计。
- 倾斜的“开闭原则” 访问者模式以一种倾斜的方式支持“开闭原则”,增加新的访问者方便,但是增加新的元素很困难。
购物车
顾客在超市中将选择的商品,如苹果、图书等放在购物车中,然后到收银员处付款。在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品。此时,购物车作为一个ObjectStructure(对象结构)用于存储各种类型的商品,而顾客和收银员作为访问这些商品的访问者,他们需要对商品进行检查和计价。不同类型的商品其访问形式也可能不同,如苹果需要过秤之后再计价,而图书不需要。使用访问者模式来设计该购物过程。