第五章 设计模式之行为型模式

设计模式说明
模版方法(TemplateMethod)模式定义一个操作中的算法骨架,而将算法的一些步骤延迟 到子类中,便得子类可以不改变该算法结构的情况下重定义核算的某些特定步骤。
策略(Strategy)模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
命令(Command)模式将一个请求封装为一个对象,便发出请求的责任和执行请求的责任分割开。
职责链(Chain of Responsibility)模式把请求从链中的一个对象传到下一个对象,知道请求被响应为止。通过这种方式去除对象之间的耦合。
状态(State)模式允许一个对象在其内部状态发生改变时改变其行为能力。
观察者(Observer)模式多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
中介者(Mediator)模式定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
迭代器(iterator)模式提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
访问者(Visitor)模式在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
备忘录(Memento)模式在不破坏封装的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
解释器(Interpreter)模式提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

模版方法(TemplateMethod)模式

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中做事的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。
以下介绍的模板方法模式将解决以上类似的问题。

模式的定义与特点

模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式.
该模式的主要优点如下。
① 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
② 它在父类中提取了公共的部分代码,便于代码复用。
③ 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

模式的结构与实现

模板方法模式需要注意抽象类与具体子类之间的协作。它用到了虚函数的多态性技术以及"不用调用我,让我来调用你"的反向控制技术。
结构
现在来介绍它们的基本结构(基本结构 = 抽象类/抽象模板(Abstract Class) + 具体子类/具体实现(Concrete Class) )

模板方法模式包含以下主要角色。
① 抽象类/抽象模板(Abstract Class):抽象模板类,负责给出一个算法的轮廓和骨架(=一个模板方法+若干个基本方法)。
② 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
③ 基本方法:是整个算法中的一个步骤,包含以下几种类型(抽象方法/具体方法/钩子方法)。
④ 抽象方法:在抽象类中声明,由具体子类实现。
⑤ 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它.
⑥ 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
也就是说钩子方法为你在实现某一个抽象类的时候提供了可选项,相当于预先提供了一个默认配置.
⑦ 具体子类/具体实现(Concrete Class):具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
模板方法模式的结构图如图1所示。
在这里插入图片描述

实现
【例1】用模板方法模式实现出国留学手续设计程序。
分析:出国留学手续一般经过以下流程:索取学校资料,提出入学申请,办理因私出国护照、出境卡和公证,申请签证,体检、订机票,准备行装,抵达目标学校等,其中有些业务对各个学校是一样的,但有些业务因学校不同而不同,所以比较适合用模板方法模式来实现.
在本实例中,我们先定义一个出国留学的抽象类StudyAbroad,里面包含了一个模板方法TemplateMethod(),该方法中包含了办理出国留学手续流程中的各个基本方法,其中有些方法的处理由于各国都一样,所以在抽象类中就可以实现,但有些方法的处理各国是不同的,必须在其具体子类(如美国留学类StudyInAmerica)中实现。如果再增加一个国家,只要增加一个子类就可以了,图2所示是其结构图。
在这里插入图片描述

模式的应用场景

模板方法模式通常适用于以下场景。
① 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现.
② 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码,
③ 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

策略(Strategy)模式

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以采用打折、送商品、送积分等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。

策略模式的定义与特点

策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

优点和缺点

① 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if…else语句、switch…case语句。
② 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码,
③ 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
④ 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
⑤ 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

策略模式的结构与实现

策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性,现在我们来分析其基本结构和实现方法
结构
策略模式的主要角色如下。
1.抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
2.具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。3.环境(Context)类:持有一个策略类的引用,最终给客户端调用。
在这里插入图片描述
分析:关于大闸蟹的做法有很多种,我们以清蒸大闸蟹和红烧大闸蟹两种方法为例,介绍策略模式的应用。
首先,定义一个大闸蟹加工的抽象策略类(CrabCooking),里面包含了一个做菜的抽象方法、CookingMethod();然后,定义清蒸大闸蟹(SteamedCrabs)和红烧大闸蟹(BraisedCrabs)的具体策略类,它们实现了抽象策略类中的抽象方法;由于本程序要显示做好的结果图,所以将具体策略类定义成JLabel的了类;最后,定义一个厨房(Kitchen))环境类,它具有设置和选择做菜策略的方法;客户类通过厨房类获取做菜策略,并把做菜结果图在窗体中显示出来,图2所示是其结构图。
在这里插入图片描述

命令(Command)模式

在软件开发系统中,“方法的请求者"与“方法的实现者"之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。
例如,想对方法进行“撤销、重做、记录"等处理都很不方便,因此,如何将方法的请求者与实现者解耦?“变得很重要,命令模式就能很好也解决这个问题。
在现实生活中,命令模式的例子也很多。比如看电视时,我们只需要轻轻一按遥控器就能完成频道的切换,这就是命令模式,将换台请求和换台处理完全解耦了。电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)。
再比如,我们去餐厅吃饭,菜单不是等到客人来了之后才定制的,而是已经预先配置好的。这样,客人来了就只需要点菜,而不是任由客人临时定制。餐厅提供的菜单就相当于把请求和处理进行了解耦,这就是命令模式的体现。

命令模式的定义与特点

命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

优点和缺点

① 通过引入中间件(抽象接口)降低系统的耦合度。
② 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足"开闭原则"。
③ 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
④ 方便实现 Undo和Redo操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的澈销与恢复.
⑤ 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

命令模式的结构与实现

结构
命令模式包含以下主要角色。
① 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法execute()。
② 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
③ 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
④ 调用者l请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
在这里插入图片描述

实现
【例1】用命令模式实现客户去餐馆吃早餐的实例。
分析:客户去餐馆可选择的早餐有肠粉、河粉和馏钝等,客户可向服务员选择以上早餐中的若干种,服务员将客户的请求交给相关的厨帅去做。这里的点早餐相当于"命令"”,服务员相当于"调用者",厨师相当于"接收者",所以用命令模式实现比较合适。
·首先,定义一个早餐类(Breakfast),它是抽象命令类,有抽象方法 cooking(),说明要做什么;
·再定义其子类肠粉类(ChangFen)、混沌类(HunTun)和河粉类(HeFen),它们是具体命令类,实现早餐类的cooking()方法,但它们不会具体做,而是交给具体的厨师去做;
·具体厨师类有肠粉厨师(ChangFenChef)、混沌厨师(HunTunChef)和河粉厨师(HeFenChef),他们是命令的接收者。
由于本实例要显示厨师做菜的效果图,所以把每个厨师类定义为 JFrame的子类;最后,定义服务员类(Water),它接收客户的做菜请求,并发出做菜的命令。客户类是通过服务员类来点菜的,图2所示是其结构图。
在这里插入图片描述

职责链(Chain of Responsibility)模式

在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。
在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有Struts2的拦截器、JSP 和Servlet 的Fiter等,所有这些,都是使用责任链模式来实现。

模式的定义与特点

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条键传递,直到有对象处理它为止。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

优点与缺点

责任链模式是一种对象行为型模式,其主要优点如下。
① 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
② 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
③ 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
④ 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if–else语句。
⑤ 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

责任链模式的结构与实现

结构
职责链模式主要包含以下角色。
1.抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
2具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
3.客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;
理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。
其结构图如图1所示。客户端可按图2所示设置责任链。
在这里插入图片描述
在这里插入图片描述
实现
【例1】用责任链模式设计一个请假条审批模块。
分析:假如规定学生请假小于或等于2天,班主任可以批准;小于或等于7天,系主任可以批准;小于或等于10天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现.
首先,定义一个领导类(Leader),它是抽象处理者,包含了一个指向下一位领导的指针next和一个处理假条的抽象处理方法handleRequest(int LeaveDays);然后,定义班主任类(ClassAdviser)、系主任类(DepartmentHead)和院长类(Dean),它们是抽象处理者的子类,是具体处理者,必须根据自己的权力去实现父类的 handleRequest(int LeaveDays)方法,如果无权处理就将假条交给下一位具体处理者,直到最后;客户类负责创建处理链,并将假条交给链头的具体处理者(班主任)。
在这里插入图片描述

观察者(Observer)模式

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。
① 某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;
② 当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。
在软件世界也是这样,例如,
① Excel中的数据与折线图、饼状图、柱状图之间的关系;
② MVC模式中的模型与视图的关系;事件模型中的事件源与事件处理者。
总结:所有这些,如果用观察者模式来实现就非常方便。

模式的定义与特点

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
主要优点如下。
1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。2.目标与观察者之间建立了一套触发机制。

模式的结构与实现

实现观察者模式时要注意
具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

结构
观察者模式的主要角色如下。
① 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
② 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
③ 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
④ 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新
自身的状态。
在这里插入图片描述

案例
【例1】利用观察者模式设计一个程序,分析“人民币汇率”的升值或贬值对进口公司进口产品成本或出口公司的出口产品收入以及公司利润率的影响。
分析:
当"人民币汇率”升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;
当“人民币汇率”贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升。
这里的汇率(Rate)类是抽象目标类,它包含了保存观察者(Company)的 List和增加/删除观察者的方法,以及有关汇率改变的抽象方法change(int number);而人民币汇率(RMBrate)类是具体目标,它实现了父类的change(int number)方法,即当人民币汇率发生改变时通过相关公司;公司(Company)类是抽象观察者,它定义了一个有关汇率反应的抽象方法 response(int number);进口公司(ImportCompany)类和出口公司ExportCompany)类是具体观察者类,它们实现了父类的 response(int number)方法,即当它们接收到汇率发生改变的通知时作为相应的反应。
在这里插入图片描述

中介者(Mediator)模式

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他所有朋友的电话;而且,朋友中如果有人的电话修改了,他必须让其他所有的朋友一起修改,这叫作“牵一发而动全身”,非常复杂。
如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。如前面所说的“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录"就解决了。这样的例子还有很多,例如,你刚刚参加工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。
在软件的开发过程中,这样的例子也很多,例如,在MC框架中,控制器©就是模型(M)和视图(V)的中介者;还有大家常用的微信聊天程序的“中介者”是微信服务器。所有这些,都可以采用“中介者模式”来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。

模式的定义与特点

中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式是一种对象行为型模式,其主要优点如下。
① 类之间各司其职,符合迪米特法则。
② 降低了对象之间的耦合性,使得对象易于独立地被复用。
③ 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
其主要缺点是:中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。

模式的结构与实现

中介者模式实现的关键是找出“中介者”,下面对它的结构和实现进行分析。
结构
中介者模式包含以下主要角色。
① 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
② 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个List 来管理同事对象,协调各个同事角色之间的交互关系。因此它依赖于同事角色。
③ 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
④ 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
在这里插入图片描述

案例
【例1】用中介者模式编写一个“韶关房地产交流平台”程序。
说明:韶关房地产交流平台是“房地产中介公司”提供给“卖方客户”与“买方客户”进行信息交流的平台,比较适合用中介者模式来实现。
首先,定义一个中介公司(Medium)接口,它是抽象中介者,它包含了客户注册方法 register(Customer member)和信息转发方法 relay(String from,String ad);
再定义一个韶关房地产中介(EstateMedium)公司,它是具体中介者类,它包含了保存客户信息的List对象,并实现了中介公司中的抽象方法。
然后,定义一个客户(Customer)类,它是抽象同事类,其中包含了中介者的对象,和发送信息的 send(String ad)方法与接收信息的 receive(String from,String ad)方法的接口,由于本程序是窗体程序,所以本类继承JPmme类,并实现动作事件的处理方法 actionPerformed(ActionEvent e).
最后,定义卖方(Seller)类和买方(Buyer)类,它们是具体同事类,是客户(Customer)类的子类,它们实现了父类中的抽象方法,通过中介者类进行信息交流,其结构图如图2所示。
在这里插入图片描述

备忘录(Memento)模式

每个人都有犯错误的时候,都希望有种“后悔药"能弥补自己的过失,让自己重新开始,但现实是残酷的。在计算机应用中,客户同样会常常犯错误,能否提供“后悔药"给他们呢?当然是可以的,而且是有必要的。这个功能由备忘录模式”来实现。
其实很多应用软件都提供了这项功能,如 Word、记事本、Photoshop、Eclipse 等软件在编辑时按Ctrl+Z组合键时能撤销当前操作,使文档恢复到之前的状态;还有在正中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。
备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的状态。

模式的定义与特点

备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

优点和缺点

备忘录模式是一种对象行为型模式,其主要优点如下。
① 提供了一种可以恢复状态的机制。
② 实现了内部状态的封装。

模式的结构与实现

结构
备忘录模式的主要角色如下。
① 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
② 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
③ 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
在这里插入图片描述

案例
【例1】利用备忘录模式设计相亲游戏。
分析:假如有西施、王昭君、貂蝉、杨玉环四大美女同你相亲,你可以选择其中一位作为你的爱人;当然,如果你对前面的选择不满意,还可以重新选择,但希望你不要太花心;这个游戏提供后悔功能,用“备忘录模式”设计比较合适。
首先,先设计一个美女(Girl)类,它是备忘录角色,提供了获取和存储美女信总的功能;
然后,设计一个相亲者(You)类,它是发起人角色,它记录当前时刻的内部状态信息(临时妻子的姓名),并提供创建备忘录和恢复备忘录数据的功能;
最后,定义一个美女栈(GirIStack)类,它是管理者角色,负责对备忘录进行管理,用于保存相亲者(You)前面选过的美女信息,不过最多只能保存4个,提供后悔功能。
客户类设计成窗体程序,它包含美女栈(GirlStack)对象和相亲者(You)对象,它实现了ActionListener接口的事件处理方法actionPerformed( ActionEvent e),并将4大美女图像和相亲者(You)选择的美女图像在窗体中显示出来。图2所示是其结构图。
在这里插入图片描述

迭代器(iterator)模式

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构"中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了“开闭原则”。
既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:
① 暴露了聚合类的内部表示,使其数据不安全;
② 增加了客户的负担。
“迭代器模式"能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和"“开闭原则”,如 Java 中的 Collection、List、Set、Map等都包含了迭代器。
迭代器模式在生活中应用的比较广泛,比如:物流系统中的传送带,不管传送的是什么物品,都会被打包成一个个箱子,并且有一个统一的二维码。这样我们不需要关心箱子里是什么,在分发时只需要一个个检查发送的目的地即可。再比如,我们平时乘坐交通工具,都是统一刷卡或者刷脸进站,而不需要关心是男性还是女性、是残疾人还是正常人等信息。

模式的定义与特点

迭代器(lterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器模式是一种对象行为型模式,其主要优点如下。
① 访问一个聚合对象的内容而无须暴露它的内部表示。
② 遍历任务交由迭代器完成,这简化了聚合类。
③ 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
④ 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
⑤ 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

模式的结构与实现

结构
迭代器模式主要包含以下角色。
① 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
② 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
③ 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next()等方法。
④ 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
在这里插入图片描述

案例
【例1】用迭代器模式编写一个浏览婺源旅游风景图的程序。
分析:婺源的名胜古迹较多,要设计一个查看相关景点图片和简介的程序,用“迭代器模式”设计比较合适。
首先,设计一个婺源景点(MyViewSpot)类来保存每张图片的名称与简介;再设计一个景点集(ViewSpotSet)接口,它是抽象聚合类,提供了增加和删除婺源景点的方法,以及获取迭代器的方法。
然后,定义一个婺源景点集(MyViewSpotSet)类,它是具体聚合类,用ArrayList 来保存所有景点信息,并实现父类中的抽象方法;再定义婺源景点的抽象迭代器(ViewSpotltemtor)接口,其中包含了查看景点信息的相关方法。
最后,定义婺源景点的具体迭代器(WwViewSpotlterator)类,它实现了父类的抽象方法;客户端程序设计成窗口程序,它初始化婺源景点集(ViewSpotSet)中的数据,并实现ActionListener接口,它通过婺源景点迭代器
(ViewSpotlterator)来查看婺源景点(WyViewSpot)的信息。图2所示是其结构图。
在这里插入图片描述

访问者(Visitor)模式

在现实生活中,有些集合对象存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。
例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个点的评价可能不同;
医院医生开的处方单中包含多种药元素,查看它的划价员和药房工作人员对它的处理方式也不同,划价员根据处方单上面的药品名和数量进行划价,药房工作人员根据处方单的内容进行抓药。
这样的例子还有很多,例如,电影或电视剧中的人物角色,不同的观众对他们的评价也不同;
还有顾客在商场购物时放在“购物车"中的商品,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量。
这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,如果用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。

模式的定义与特点

访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者( Visitor)模式是一种对象行为型模式,其主要优点如下。
① 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
② 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
③ 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
④ 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单

模式的结构与实现

访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类,其基本结构与实现方法如下。
结构
访问者模式包含以下主要角色。
① 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作visit(),该操作中的参数类型标识了被访问的具体元素。
② 具体访问者((ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
③ 抽象元素(Element》角色:声明一个包含接受操作accept()的接口,被接受的访问者对象作为accept()方法的参数。
④ 具体元素(ConcreteElement)角色:实现抽象元素角色提供的accept()操作,其方法休通常都是visitor.visit(this).另外具体元素中可能还包含本身业务逻辑的相关操作。
⑤ 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由List、Set、Map等聚合类实现。
其结构图如图1所示。
在这里插入图片描述

案例
【例1】利用“访问者( Visitor)模式”模拟艺术公司与造币公司的功能。
分析:艺术公司利用“铜”可以设计出铜像,利用“纸”可以画出图画;造币公司利用“铜"可以印出铜币,利用“纸"”可以印出纸币。对“铜”和“纸”这两种元素,两个公司的处理方法不同,所以该实例用访问者模式来实现比较适合。
首先,定义一个公司(Company)接口,它是抽象访问者,提供了两个根据纸(Paper)或铜(Cuprum)这两种元素创建作品的方法;再定义艺术公司(ArtCompany)类和造币公司(Mint)类,它们是具体访问者,实现了父接口的方法。
然后,定义一个材料(Material)接口,它是抽象元素,提供了accept (Company visitor)方法来接受访问者
(Company)对象访问;再定义纸(Paper)类和铜(Cuprum)类,它们是具体元素类,实现了父接口中的方法。
最后,定义一个材料集(SetMaterial)类,它是对象结构角色,拥有保存所有元素的容器List,并提供让访问者对象遍历容器中的所有元素的 accept (Company visitor)方法;客户类设计成窗体程序,它提供材料集(SetMaterial)对象供访问者(Company)对象访问,实现了ItemListener接口,处理用户的事件请求。图2所示是其结构图。
在这里插入图片描述

解释器(Interpreter)模式

在软件开发中,会遇到有些问题多次重复出现。而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了,
虽然使用解释器模式的实例不是很多.但对于满足以上特点。且对运行效率要求不是很高的应用实例,如果用解释器模式来实现,其效果是非常好的。

模式的定义与特点

解释器〈Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口、该接口解释一个特定的上下文。
这里提到的文法和句子的概念同编译原理中的描述相同,“文法*"指语言的语法规则,而“句子“是语言集中的元素。例如,
汉语中的句子有很多、“我是中国人"是其中的一个句子,可以用一探语法树来直观地描述语言中的句子。

优点和缺点

解释器模式是一种类行为型模式.其主要优点如下。
① 扩展性好。由于在解释器模式中使用类来表示语言的文法规则。因此可以通过继承等机制来改变或扩展文法。
② 容易实现。在语法树中的每个表达式节点类都是相似的·所以实现其文法较为容易。

模式的结构与实现

解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。
1)文法
文法是用于描述语言的语法结构的形式规则。没有规矩不成方圆,例如,有些人认为完美爱情的准则是“相互吸引、感情专一、任何一方都没有恋爱经历”,虽然最后一条准则较苛刻,但任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。
例如,中文中的“句子”的文法如下。
(句子)::=〈主语》〈谓语》〈宾语>
《主语)::=〈代词)|〈名词》
〈谓语〉::=〈动词)
《宾语〉::=〈代词)|〈名词>〈代词〉你|我|他
《名词)7大学生I筱霞I英语《动词〉::=是|学习
注:这里的符号“::=”表示“定义为”的意思,用“〈”和“)”括住的是非终结符,没有括住的是终结符。

终结符:
可理解为一个可拆分元素
非终结符:
是不可拆分的最小元素
(2)句子
句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。例如,上述文法可以推出“我是大学生”,折以它是句子。
在这里插入图片描述
图:我是大学生

有了以上基础知识,现在来介绍解释器模式的结构就简单了。解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。

结构
解释器模式包含以下主要角色。
①抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法interpretO.
② 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
③ 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
④ 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
⑤ 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
在这里插入图片描述

案例
【例1】用解释器模式设计一个“韶粤通”公交车卡的读卡器程序。
说明:假如“韶粤通"公交车读卡器可以判断乘客的身份,如果是“韶关"或者“广州”的“老人”“妇女”“儿童”就可以免费乘车,其他人员乘车一次扣2元。
分析:本实例用“解释器模式”设计比较适合,首先设计其文法规则如下。
::= 的
::=韶关|广州
::=老人|妇女|儿童
选择语言
然后,根据文法规则按以下步骤设计公交车卡的读卡器程序的类图。
·定义一个抽象表达式(Expression)接口,它包含了解释方法 interpret(String info)。
·定义一个终结符表达式(Terminal Expression)类,它用集合(Set)类来保存满足条件的城市或人,并实现抽象表达式接口中的解释方法interpret(Stringinfo),用来判断被分析的字符串是否是集合中的终结符。
定义一个非终结符表达式( AndExpressicm)类,它也是抽象表达式的子类,它包含满足条件的城市的终结符表达式对象和满足条件的人员的终结符表达式对象,并实现 interpret(String info)方法,用来判断被分析的字符串是否是满足条件的城市中的满足条件的人员。
最后,定义一个环境(Context)类,它包含解释器需要的数据,完成对终结符表达式的初始化,并定义一个方法freeRide(String info)调用表达式对象的解释方法来对被分析的字符串进行解释。
在这里插入图片描述

状态(State)模式

在软件开发过程中,应用程序中的部分对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的1个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为也发生改变。如人都有高兴和伤心的时候,不同的情绪有不同的行为。
对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用if-else或 switch-case语句来做状态判断,再进行不同情况的处理。但是显然这种做法对复杂的状态判断存在天然弊端,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else语句,这违背了“开闭原则”.不利于程序的扩展。
以上问题如果采用“状态模式"就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑"提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。

状态模式的定义与特点

状态(State)模式的定义:对有状态的对象,把复杂的"判断逻辑”提取到不同的状态对象咂,允许状态对象在其内部状态发生改变时改变其行为。
状态模式是一种对象行为型模式,其主要优点如下。
① 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足"单一职责原则”。
② 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
③ 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

状态模式的结构与实现

结构
状态模式包含以下主要角色。
① 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
② 抽象状态〈State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
③ 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
在这里插入图片描述

案例
【例1】用“状态模式”设计一个学生成绩的状态转换程序。
分析:本实例包含了“不及格""中等”和"优秀”3种状态,当学生的分数小于60分时为“不及格"状态,当分数大于等于60分且小于90分时为“中等”状态,当分数大于等于90分时为“优秀”状态,我们用状态模式来实现这个程序。
首先,定义一个抽象状态类(AbstractState),其中包含了环境属性、状态名属性和当前分数属性,以及加减分方法addScore(intx)和检查当前状态的抽象方法 checkStateo.
然后,定义“不及格”状态类LowState、“中等”状态类MiddleState和“优秀”状态类HighState,它们是具体状态类,实现checkStateO方法,负责检查自己的状态,并根据情况转换。
最后,定义环境类(ScoreContext),其中包含了当前状态对象和加减分的方法 add(int score),客户类通过该方法来改变成绩状态。图2所示是其结构图。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-攀-登-者-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值