行为型设计模式实验

实验目的

  1. 结合实例,熟练绘制常见的行为型设计模式结构图
  2. 结合实例,熟练使用任意一种面向对象编程语言实现常见的行为型设计模式
  3. 通过本实验,理解每一种行为型设计模式的模式动机,掌握模式结构,学习如何使用代码实现这些设计模式

实验要求

  1. 结合实例,绘制常见行为型设计模式的结构图
  2. 使用任意一种面向对象编程语言实现常见行为型设计模式实例,代码运行正确

实验内容

练习1

在某Web框架中采用职责链模式来组织数据过滤器,不同的数据过滤器提供了不同的功能,例如字符编码转换过滤器、数据类型转换过滤器、数据校验过滤器等,可以将多个过滤器连接成一个过滤器链,进而对数据进行多次处理。根据以上描述,绘制对应的类图并编程模拟实现

使用责任链模式,创建一个接收者对象的链,对请求的发送者和接收者进行解耦。如果一个对象不能处理该请求,那么就会把相同的请求传给下一个接收者,以此类推。首先创建WebDataFilter抽象数据过滤类,其中需要存储后继过滤类以及抽象过滤方法;创建CharCodeFilterDataTypeFilter以及DataCheckFilter等具体抽象类全部继承WebDataFilter类实现相关过滤功能

类图与实现代码如下所示

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package dp_lab4_q1;
public abstract WebDataFilter {
private WebDataFilter successor;
public abstract void filter();
}
public class CharCodeFilter extends WebDataFilter {
public void filter() {
// 实现字符编码转换过滤器的功能
}
}
public class DataTypeFilter extends WebDataFilter {
public void filter() {
// 实现数据类型转换过滤器的功能
}
}
public class DataCheckFilter extends WebDataFilter {
public void filter() {
// 实现数据校验转换过滤器的功能
}
}

练习2

某灯具厂商要生产一个智能灯具遥控器,该遥控器具有5个可编程的插槽,每个插槽都有一个控制灯具的开关,这5个开关可以通过蓝牙技术控制5个不同房间灯光的打开和关闭,用户可以自行设置每一个开关多对应的房间。现采用命令模式实现该智能遥控器的软件部分,绘制对应的类图并编程模拟实现

使用命令模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。客户端Client用来创建一个具体命令对象并确定其接收者;首先创建Command命令接口以及ConcreteCommand接口实现类;创建Light命令接收者作为房间的灯光;创建Switch请求者类作为控制灯具的开关。由Swithch发送命令,Light接收命令,实现控制灯光的功能

类图与实现代码如下所示

2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package dp_lab4_q2;
public interface Command {
void execute1();
void execute2(int Room);
}
public class Light {
private int Room;
private String State;
public static Map<int,Light> LightMap=new HashMap<>();
public Light(int Room) {
this.Room=Room;
this.State="off";
}
public void switchState() {
if(State.equals("off")) State="on";
else if(State.equals("on")) State="off";
}
}
public class Switch {
private Command command;
public void switchState() {
command.execute1();
}
public void changeLight(int Room) {
command.execute2(Room);
}
}
public class ConcreteCommand implements Command {
private Light light;
public void execute1() {
// 执行命令1 打开/关闭灯光
light.switchState();
}
public void execute2(int Room) {
// 执行命令2 设置所控制的灯光
this.light=Light.LightMap.get(Room);
if(light==null) {
light=new Light(Room);
Light.LightMap.put(Room,light);
}
}
}

练习3

某软件公司要为数据库备份和同步开发一套简单的数据库同步指令,通过指令可以对数据库中的数据和结构进行备份。例如,输入指令"COPY VIEW FROM srcDB TO desDB",表示将数据库srcDB中的所有视图(View)对象都拷贝至数据库desDB;输入命令"MOVE TABLE Student FROM srcDB TO desDB",表示将数据库srcDB中的Student表移动至数据库desDB。现使用解释器模式来设计并编程模拟实现该数据库同步指令系统

使用解释器模式,实现表达式接口,解释一个特定的命令。首先创建一个Context作为被解释类;创建一个Expression表达式接口以及TermonalExpressionNonTerminalExpression两种针对终结符表达式和非终结符表达式的数据库指令实现类

类图与实现代码如下所示

3

1
2
3
4
5
6
7
8
9
10
11
12
package dp_lab4_q3;
public class Context {}
public interface Expression {
public void interpret(Context context);
}
public class TerminalExpression implements Expression {
public void interpret(Context context) {}
}
public class NonTerminalExpression implements Expression {
private Context childExpression;
public void interpret(Context context) {}
}

练习4

设计一个逐页迭代器,每次可返回指定个数(一页)元素,并将该迭代器用于对数据进行分页处理。绘制对应的类图并编程模拟实现

使用迭代器模式,用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。抽线创建AbstractIterator迭代器接口,其中关键方法为hasNext()以及next();创建PageIterator作为迭代器类,实现迭代功能;创建Page类来存储指定个数的元素并作为迭代单位,数据用字符串来模拟

类图与实现代码如下所示

4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package dp_lab4_q4;
public class Page {
private String[] data=new String[256]; // 模拟页中存储的数据
}
public interface AbstractIterator {
public boolean hasNext();
public Object next();
}
public class PageIterator implements AbstractIterator {
private int index;
private Page[] page;
public PageIterator(Page[] page) {
this.index=0;
this.page=page;
}
public boolean hasNext() {
if(page[index+1]!=null) return true;
return false;
}
public Object next() {
this.index++;
return this.page[index];
}
}

练习5

为了大力发展旅游业,某城市构建了一个旅游综合信息系统,该系统包括旅行社子系统(Travel Companies Subsystem)、宾馆子系统(Hotels Subsystem)、餐厅子系统(Resturants Subsystem)、机场子系统(Airport Subsystem)、旅游景点子系统(Tourism Attractions Subsystem)等多个子系统,通过该旅游综合信息系统,各类企业之间可实现信息共享,一家企业可以将客户信息传递给其他合作伙伴。由于这些子系统之间存在较为复杂的交互关系,现采用中介者模式为该旅游综合信息系统提供一个高层设计,绘制对应的类图并编程模拟实现

使用中介者模式,降低多个对象和类之间的通信复杂性,通过提供一个中介类,来处理不同类之间的通信。首先创建TravelSystem中介类来与各子系统进行交互,其中主要包括与各个子系统交互的接口方法;创建TravelCompaniesSubsystemHotelsSubsystemResturantsSubsystemAirportSubsystem以及TourismAttractionsSubsystem作为旅游系统的各个子系统类;创建TravelSubsystem抽象子系统类作为各个子系统的父类

类图与实现代码如下所示

5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package dp_lab4_q5;
public abstract class TravelSubsystem {
public final int id;
public void work();
}
public class TravelSystem {
private Map<int,TravelSubsystem> subsystemMap=new HashMap<>();
public void workWithOther(int id) {
subsystemMap.get(id).work();
}
public TravelSystem {
this.subsystemMap.put(1,new TravelCompaniesSubsystem());
this.subsystemMap.put(2,new HotelsSubsystem());
this.subsystemMap.put(3,new ResturantsSubsystem());
this.subsystemMap.put(4,new AirportSubsystem());
this.subsystemMap.put(5,new TourismAttractionsSubsystem());
}
}
public class TravelCompaniesSubsystem extends TravelSubsystem {
public void work() {
// 实现旅行社子系统的功能
}
public TravelCompaniesSubsystem {
this.id=1;
}
}
public class HotelsSubsystem extends TravelSubsystem {
public void work() {
// 实现宾馆子系统的功能
}
public HotelsSubsystem {
this.id=2;
}
}
public class ResturantsSubsystem extends TravelSubsystem {
public void work() {
// 实现餐厅子系统的功能
}
public HotelsSubsystem {
this.id=3;
}
}
public class AirportSubsystem extends TravelSubsystem {
public void work() {
// 实现机场子系统的功能
}
public AirportSubsystem {
this.id=4;
}
}
public class TourismAttractionsSubsystem extends TravelSubsystem {
public void work() {
// 实现旅游景点子系统的功能
}
public TourismAttractionsSubsystem {
this.id=5;
}
}

练习6

某文字编辑软件必须提供撤销(Undo)和重做/恢复(Redo)功能,并且该软件可支持文档对象的多步撤销和重做。开发人员决定采用备忘录模式来实现该功能,在实现过程中引入栈(Stack)作为数据结构。在实现时,可以将备忘录对象保存在两个栈中,一个栈包含用于实现撤销操作的状态对象,另一个栈包含用于实现重做操作的状态对象。在实现撤销操作时,会弹出撤销栈栈顶对象以获取前一个状态并将其设置给应用程序;同样,在实现重做操作时,会弹出重做栈栈顶对象以获取下一个状态并将其设置给应用程序。绘制对应的类图并编程模拟实现

使用备忘录模式,在不破坏封装性的前提下,保存一个对象的某个状态,以便在适当的时候恢复对象,可实现系统的撤销与重做功能。首先创建Memento类来存储编辑软件的状态,其中主要包括存储的信息以及转换为当前状态的方法;创建UndoStack以及RedoStack作为撤销栈和重做栈,用来存储已保存的Memento实例对象;创建Stack抽象类作为撤销栈和重做栈的父类;创建Editor类来模拟编辑软件,其中主要包括save()undo()以及redo()方法用来实现存储当前状态/撤销/重做的功能,用字符串来模拟编辑软件的内容

类图与实现代码如下所示

6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package dp_lab4_q6;
public class Memento {
private String content;
private void toThisState(Editor editor) {
editor.content=this.content;
}
public Memento(String content) {
this.content=content;
}
}
public abstract class Stack {
public static void push(Memento memento);
public static Memento pop();
}
public class UndoStack extends Stack {}
public class RedoStack extends Stack {}
public class Editor {
private String content;
private void save() {
UndoStack.push(new Memento(this.content));
}
private void undo() {
Memento memento=UndoStack.pop();
memento.toThisState(this);
RedoStack.push(memento);
}
private void redo() {
Memento memento=RedoStack.pop();
memento.toThisState(this);
UndoStack.push(memento);
}
}

练习7

某文字编辑软件须提供如下功能:在文版编辑窗口中包含一个可编辑文本区和3个文本信息统计区,用户可以在编辑文本区对文本进行编辑操作,第一个文本信息统计区用于显示可编辑文本区中出现的单词总数量和字符总数量,第二个文本信息统计区用于显示可编辑文本区中出现的单词(去重后按照字典排序),第三个文本信息统计区用于按照出现频次降序显示可编辑文本区中出现的单词以及每个单词出现的次数(例如 hello:5)。现采用观察者模式设计该功能,绘制对应的类图并编程模拟实现

使用观察者模式,当对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。首先创建Observer抽象类;创建TextObserver1TextObserver2以及TextObserver3继承Observer作为具体实现类,代表三个文本信息统计区,实现文本信息的监控功能;创建TextEditor类来模拟文字编辑软件

类图与实现代码如下所示

7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package dp_lab4_q7;
public abstract class Observer {
protected TextEditor textEditor;
public abstract void observe();
}
public class TextEditor {
private List<Observer> observerList=new ArrayList<>();
public void attach(Observer observer) {
// 增加观察者
observerList.add(observer);
}
public void notifyAllObservers() {
// 通知所有观察者
for(Observer observer : observerList) {
observer.observe();
}
}
}
public class TextObserver1 extends Observer {
public TextObserver1(TextEditor textEditor) {
this.textEditor=textEditor;
this.textEditor.attach(this);
}
public void oberve() {
// 显示可编辑文本区中出现的单词总数量和字符总数量
}
}
public class TextObserver2 extends Observer {
public TextObserver2(TextEditor textEditor) {
this.textEditor=textEditor;
this.textEditor.attach(this);
}
public void oberve() {
// 显示可编辑文本区中出现的单词(去重后按照字典排序)
}
}
public class TextObserver3 extends Observer {
public TextObserver3(TextEditor textEditor) {
this.textEditor=textEditor;
this.textEditor.attach(this);
}
public void oberve() {
// 按照出现频次降序显示可编辑文本区中出现的单词以及每个单词出现的次数
}
}

练习8

在某网络管理软件中, TCP 连接(TCP Connection)具有建立(Established)、监听(Listening)、关闭( Closed )等多种状态,在不同的状态下 TCP 连接对象具有不同的行为,连接对象还可以从一个状态转换到另一个状态。当一个连接对象收到其他对象的请求时,它根据自身的当前状态做出不同的反应。现采用状态模式对 TCP 连接进行设计,绘制对应的类图并编程模拟实现

使用状态模式,类的行为是基于状态而改变的。首先创建TCPState状态接口;创建EstablishedStateListeningState以及ClosedState接口实现类来模拟TCP连接的建立、监听和关闭等状态;创建Connector类来模拟连接对象,会根据自身状态做出不同反应

类图与实现代码如下所示

8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package dp_lab4_q8;
public interface TCPState {
public void act();
}
public class EstablishedState implements TCPState {
public void act() {
System.out.println("Established State");
}
}
public class ListeningState implements TCPState {
public void act() {
System.out.println("Listening State");
}
}
public class ClosedState implements TCPState {
public void act() {
System.out.println("Closed State");
}
}
public class Connector {
private TCPState state=null;
public void react() {
// 根据状态做出反应
if(this.state==null) System.out.println("NULL");
else if(this.state instanceof EstablishedState) state.act();
else if(this.state instanceof ListeningState) state.act();
else if(this.state instanceof ClosedState) state.act();
else System.out.println("ERROR!");
}
}

练习9

在某云计算模拟平台中提供了多种虚拟机迁移算法,例如动态迁移算法中的 Pre - Copy (预拷贝)算法、 Post - Copy (后拷贝)算法、 CR / RT - Motion 算法等,用户可以灵活地选择所需的虚拟机迁移算法,也可以方便地增加新算法。现采用策略模式进行设计,绘制对应的类图并编程模拟实现

使用策略模式,一个类的行为或其算法可以在运行时更改。首先创建Algorithm作为策略算法抽象类;创建PreCopyAlgorithmPostCopyAlgorithm以及MotionAlgorithm作为策略算法实现类;创建VirtualMachine类来模拟虚拟机

类图与实现代码如下所示

9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package dp_lab4_q9;
public abstract class Algorithm {
protected int id;
public abstract void execute();
}
public class VirtualMachine {
private Algorithm algorithm;
private Map<I,Algorithm> algorithmMap=new HashMap<>();
public void migrate() {
algorithm.execute();
}
public void alterAlgorithm(int id) {
Algorithm algorithm=algorithmMap.get(id);
if(algorithm!=null) {
this.algorithm=algorithm;
}
else System.out.println("ERROR!");
}
public void addAlgorithm(Algorithm algorithm) {
if(algorithmMap.containsValue(algorithm)||algorithmMap.containsKey(algorithm.id)) {
System.out.println("ERROR!");
}
else {
algorithmMap.put(algorithm.id, algorithm);
}
}
}
public class PreCopyAlgorithm extends Algorithm {
public void execute() {
// 执行预拷贝算法
System.out.println("Pre Copy Algorithm");
}
}
public class PostCopyAlgorithm extends Algorithm {
public void execute() {
// 执行后拷贝算法
System.out.println("Post Copy Algorithm");
}
}
public class MotionAlgorithm extends Algorithm {
public void execute() {
// 执行CR/RT-Motion算法
System.out.println("CR/RT-Motion Algorithm");
}
}

练习10

在某数据挖掘工具的数据分类模块中,数据处理流程包括4个步骤,分别是:①读取数据;②转换数据格式;③调用数据分类算法;④显示数据分类结果。对于不同的分类算法而言,第①步、第②步和第④步是相同的,主要区别在于第③步。第③步将调用算法中已有的分类算法实现,例如朴素贝叶斯分类( Naive Bayes )算法、决策树( DecsionTree 算法, K 最近邻( K - Nearest Neighbor , KNN )算法等。现采用模板方法模式和适配器模设计该数据分类模块,绘制对应的类图并编程模拟实现

使用模板方法模式和适配器模式,一个抽象类公开定义了执行它的方法的方式/模板,子类按需重写方法实现;建立两个不兼容接口之间的桥梁,结合两个独立接口的功能。使用模板方法模式实现调用数据分类算法,使用适配器模式连接前后两步。首先创建Classify抽象分类算法类;创建NaiveBayesDecisionTree以及KNN表示分类算法类的具体实现类;创建DataMediator数据适配器类,用来获取从上一步收到的数据,在第三步中调用数据分类算法后,将所得到的数据传给下一步来显示数据分类结果;创建Step作为步骤父类;创建Step1Step2Step3以及Step4类用来模拟数据分类模块中的四个步骤

类图与实现代码如下所示

10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package dp_lab4_q10;
public abstract Classify {
public abstract void classify();
}
public class NaiveBayes extends Classify {
public void classify() {
// 朴素贝叶斯分类算法
}
}
public class DecisionTree extends Classify {
public void classify() {
// 决策树分类算法
}
}
public class KNN extends Classify {
public void classify() {
// K最邻近分类算法
}
}
public class DataMediator {
private String data; // 用字符串来模拟从第上一步得到的数据
private Classify classify; // 使用Classify类调用相应算法对数据进行分类
public void fromStep(Step step) {
// 从上一步中获取数据
this.data=step.data;
}
public void toStep(Step step) {
// 将处理好的数据传给下一步
step.data=this.data;
}
}
public class Step {
private String data;
private DataMediator dataMediator;
}
public class Step1 extends Step {}
public class Step2 extends Step {}
public class Step3 extends Step {}
public class Step4 extends Step {}

练习11

某软件公司需要设计一个源代码解析工具,该工具可以对源代码进行解析和处理,在该工具的初始版本中,主要提供了以下3个功能。
(1)度量软件规模。可以统计源代码中类的个数、每个类属性的个数以及每个类方法的个数等。
(2)提取标识符名称,以便检查命名是否合法和规范。可以提取类名、属性名和方法名等。
(3)统计代码行数。可以统计源代码中每个类和每个方法中源代码的行数。
将来还会在工具中增加一些新功能,为源代码中的类、属性和方法等提供更多的解析操作。现采用访问者模式设计该源代码解析工具,可将源代码中的类、属性和方法等设计为待访问的元素,上述不同功能由不同的具体访问者类实现,绘制对应的类图并编程模拟实现

使用访问者模式,使用一个访问这类,改变元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。首先创建Visitor作为访问者接口,其中主要好包括度量软件规模、提取标识符名称以及统计代码行数的方法;创建AnalyzerVisitor作为Visitor接口的具体实现类,来模拟源代码解析工具;创建Code类来代表数据结构,模拟被访问者,即需要被解析和处理的代码,用字符串来表示;在将来如果要增加新的功能来提供更多的解析操作,可修改Visitor接口,新增解析方法,然后在AnalyzerVisitor类中具体实现即可

类图与实现代码如下所示

11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package dp_lab4_q11;
public class Code {
private String content;
// 模拟将要被解析和处理的代码
// 也可改为读取具体文件等
}
public interface Visitor {
// 度量软件规模
public void measureSoftwareScale(Code code);
// 提取标识符名称
public void extractIdentifierName(Code code);
// 统计代码行数
public void countCodeLines(Code code);
// 如果需要新增分析方法, 可修改此接口, 增加相应函数, 然后在具体实现类中实现
}
public class AnalyzerVisitor implements Visitor {
// 在实现类中来具体实现目前已有的三种功能
public void measureSoftwareScale(Code code) {
System.out.println("measureSoftwareScale");
}
public void extractIdentifierName(Code code) {
System.out.println("extractIdentifierName");
}
public void countCodeLines(Code code) {
System.out.println("countCodeLines");
}
}

上述设计方案不符合单一职责原则,可以把Visitor接口中的方法改为visit(),用多个实现类实现不同的功能

改进后的类图与实现代码如下所示

12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package dp_lab4_q11_better;
public class Code {
private String content;
// 模拟将要被解析和处理的代码
// 也可改为读取具体文件等
}
public interface Visitor {
public void visit(Code code);
}
public class MeasureSoftwareScale implements Visitor {
// 度量软件规模
public void visit(Code code) {
System.out.println("measureSoftwareScale");
}
}
public class ExtractIdentifierName implements Visitor {
// 提取标识符名称
public void visit(Code code) {
System.out.println("extractIdentifierName");
}
}
public class CountCodeLines implements Visitor {
// 统计代码行数
public void visit(Code code) {
System.out.println("countCodeLines");
}
}
// 如果需要新增分析方法, 无需修改接口, 只需创建新的接口实现类即可

实验小结

在做一个项目之前的需求分析过程中就应该构思好整个系统的完整架构,运用的模式,以及类与类,类与接口之间的关系,以免造成不必要的麻烦。设计时要尽可能符合七大开发原则。对扩展开发而对修改关闭;面向接口编程,养成封装类以及使用抽象方法的习惯;控制每个类的粒度大小,尽量符合高内聚,低耦合的特性;确保父类所拥有的性质在子类中仍然成立;给每个类都建立他们需要的专用接口;每个类尽量只与具有直接关系的类进行交互;尽量使用组合或聚合的关联关系来实现系统的具体功能

在练习1中,使用责任链模式,创建一个接收者对象的责任链,对请求的发送者和接收者进行解耦。如果一个对象不能处理该请求,那么就会把相同的请求传给下一个接收者,以此类推,直到找到一个可以处理该请求的对象或者到责任链结束时终止

在练习2中,使用命令模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象收到命令以后去执行。命令模式的顺序为调用者→命令→接收者,降低了系统的耦合度,并且很容易将新的命令添加到系统中

在练习3中,使用解释器模式,实现表达式接口,解释一个特定的命令。可以根据实例对于一些固定文法构建一个解释句子的解释器,得到句子的具体语义,根据语义调用系统中相应的方法实现具体功能

在练习4中,使用迭代器模式,可顺序访问集合对象的元素,遍历一个聚合对象,不需要直到具体的底层表示。在实例中,使用for循环可实现相同的功能,但是迭代器模式的好处在于,可以将遍历与实现分离开来,不会暴露系统内部被迭代对象具体的数量,降低风险

在练习5中,使用中介者模式,降低多个对象和类之间的通信复杂性,通过提供一个中介类,来处理不同类之间的通信,降低了系统中的复杂性。降低耦合性,将多个同事对象的交互封装在中介者对象中,集中控制交互,集中管理多个同时对象,便于修改

在练习6中,使用备忘录模式,在不破坏系统封装性的前提下,保存一个对象的某个状态,以便在适当的时候恢复对象。可用于实现系统的撤销与重做功能,普通软件的撤销操作,游戏的存档功能以及数据库软件中事务管理的回滚操作

在练习7中,使用观察者模式,当对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。当系统涉及事件的多级触发,联合行为等场景时可使用观察者模式,使用面向对象的编码风格创建一套触发机制,广播通知所有观察者进行更新,不会影响后续的具体功能的实现

在练习8中,使用状态模式,允许对象在内部状态发生改变时改变它的行为。对外隐藏实现细节,封装了转换规则,枚举可能的状态。创建状态接口,将某个状态的行为与属性放在接口实现类当中,只需改变状态即可改变状态的行为,并且可以很方便的增加新的状态或修改现有的状态

在练习9中,使用策略模式,一个类的行为或其算法可以在运行时更改。在实例中创建算法抽象类,算法类继承算法抽象类具体实现,可以很方便地修改现有的算法或增加新的算法。系统运行时可以自由切换,无需使用多重条件判断语句来选择算法,拥有良好的扩展性

在练习10中,使用模板方法模式和适配器模式,一个抽象类公开定义了执行它的方法的方式/模板,子类按需重写方法实现;建立两个不兼容接口之间的桥梁,结合两个独立接口的功能。在实例中,步骤的内容大致相同,只有在对数据进行分类处理时才需要调用分类算法,其中分类算法有多种,使用模板方法模式实现,在步骤间数据的传递使用适配器模式实现

在练习11中,使用访问者模式,使用访问者类,改变元素类的执行算法,将数据结构与数据操作分离。在实例中,有多种对于代码的分析与处理,将操作方法封装在接口中,通过具体类来实现,便于修改现有的方法或增加新的分析与处理操作。符合单一职责原则,具有优秀的扩展性与灵活性