设计模式行为模式(二)_第1页
设计模式行为模式(二)_第2页
设计模式行为模式(二)_第3页
设计模式行为模式(二)_第4页
设计模式行为模式(二)_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第7章 行为模式(二)7.1 模板方法模式(Template)7.1.1 概念1、名称Template Method模式也叫模板方法模式,它把具有特定步骤算法中的某些必要的处理委让给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为。在作为抽象类的父类里,定义了一个具有固定算法并可以细分为多个步骤的模板方法(public),Template Method模式把这些可以被细分的可变步骤抽象为可以被子类重载的抽象方法(protected abstract),并通过在子类中的重载(重新定义),做到无需改变模板方法的算法步骤而可以重新定义该算法中的某些特定的步骤。2、结构Template M

2、ethod模式的结构如图7-1所示。图7-1 TemplateMethod模式结构3、意图准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。4、适用性模板方法适用于以下情况:l 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。l 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。其实这可以说是一种好的编码习惯了。l 控制子类扩展。模板方法只在特定点调用操作,这样就只允许在这些点进行扩展。比如上面runBare()方法就只在runTe

3、st前面适用setUp方法。如果你不愿子类来修改你的模板方法定义的框架,你可以采用两种方式来做:一是在API中不体现出你的模板方法;二、将你的模板方法置为final就可以了。可以看出,使用模板方法模式可以将代码的公共行为提取出来,达到复用的目的。而且,在模板方法模式中,是由父类的模板方法来控制子类中的具体实现。这样你在实现子类的时候,根本不需要对业务流程有太多的了解。5、角色l 抽象模版(AbstractClass)角色 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。定义并实现了一个模版方法。这个模版方法一般是一个具体方法,它给出了一个顶级逻

4、辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。l 具体模版(ConcreteClass)角色实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。每一个抽象模版角色都可以有任意多个具体模版角色与之对应,而每一个具体模版角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。7.1.2 实现让我们先来看一个具体的关于报税的简单例子。其结构如图7-2所示。图7-2 结构图package com.sanlian.mode.templateMethod;/* * 保税 * author sanlia

5、n * version Nov 23, 2010 2:51:28 PM */public abstract class BasicTax protected float income;/ 你的收入public void reportTax() / 计算一下你该给省政府交多少税float sTax = calculateProvinceTax();/ 计算一下你该给国家政府交多少税float fTax = calculateChinaTax();/ 算算你破产没boolean ok = checkBankBalance(sTax + fTax);if (!ok) / 申请破产fileBankru

6、ptcy(); else / 如果没破产就把税交上吧sendMoneyToGov(sTax + fTax);protected abstract float calculateProvinceTax();protected abstract float calculateChinaTax();/* * 检查银行帐号是否破产 * param tax * return */private boolean checkBankBalance(float tax)return false;/* * 申请破产 */private void fileBankruptcy()System.out.printl

7、n(已破产.);/* * 上交税金 */private void sendMoneyToGov(float tax)System.out.println(上交税金:+tax);事实上,不同的省政府设定的税率或是计算方法有可能是不同的,国家政府在某些时期对税率也会有所调整,所以BasicTax抽象类的创建者不必关心给个省政府的税率是如何的,也不必关心国家政府会在什么时候调整税率,他只要知道报税人要么报税要么申请破产就可以了,BasicTax是个抽象类,你没有办法直接实例化它,你要做的就是创建一个继承自BasicTax的派生类,并实现calculateProvinceTax和calculateCh

8、inaTax这两个抽象方法。/* * 上海的报税具体实现 * author sanlian * version Nov 23, 2010 2:51:02 PM */public class ShangHaiTax extends BasicTax Overrideprotected float calculateChinaTax() return income * 0.23F;Overrideprotected float calculateProvinceTax() return income * 0.12F;/* * 重庆报税具体实现类 * author sanlian * version

9、 Nov 23, 2010 2:56:57 PM */public class ChongQingTax extends BasicTax Overrideprotected float calculateChinaTax() return income*0.23F;Overrideprotected float calculateProvinceTax() return income*0.24F;从上面的代码中你可以看到,ShangHaiTax和ChongQingTax分别为上海和重庆报税方法提供了具体的实现:package com.sanlian.mode.templateMethod;/

10、* * 测试类 * author sanlian * version Nov 23, 2010 2:58:42 PM */public class Test public static void main(String args) System.out.println(-one-);BasicTax one = new ShangHaiTax();one.reportTax();System.out.println(-two-);BasicTax two = new ChongQingTax();two.reportTax();运行结果如图7-3所示。图7-3 运行结果好了让我们来回顾一下,在

11、BasicTax的例子中ReportTax其实就是一个模板方法,这个方法描述了报税时所要执行的步骤,却没有描述每一步是如何执行的,因为它所依赖的方法还没有实现。模板方法强调的是不同对象之间及方法之间如何协作。在实际情况中,模板方法通常包含了业务领域知识,这些领域知识规定了不同方法是如何协作的;抽象方法为模板方法调用的方法提供了客户化实现的手段。模板方法描述了特定操作步骤的骨架或是处理流程,而不是直接规定给个操作该如何执行,将抽象与具体分割开来,从而保证代码可以最大限度地被复用。7.2 策略模式(Strategy Pattern)7.2.1 概念策略模式(Strategy Pattern)中体面

12、了两个非常基本的面向对象设计的基本原则:封装变化的概念;编程中是哟个接口,而不是对接口实现。策略模式是开发人员能够开发出由许多可替换的部分组成的软件,并且各个部分之间是弱连接的关系。弱连接的特性使软件具有更强的可扩展性,易于维护;更重要的是,它大大提高了软件的可重用性。1、名称策略模式就是定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。策略模式使这些算法在客户端调用它们的时候能够互不影响。2、结构策略模式的结构如图7-4所示。图7-4 策略模式结构图3、意图定义以系列的算法,把他们一个个封装起来,并且使它们可相互替换。本模式的算法可以独立于使用它的客户而变化。4、适用性l 血多相关

13、的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。l 需要使用一个算法的不同变体。例如,您可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。l 算法封装客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。l 一个类定义了多种行为,并切这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的策略类中以代替这些条件语句。5、策略模式中的各个角色l Context超类:定义了所有需要继承这个类的子类的属性和方法,并且定义了改变这个父类变化行为的方法。l 算法接口:定义了具体

14、行为的方法声明,由这个接口的实现类来具体实现接口定义的行为方法。l 具体算法类:一组具体的算法接口实现类,代表不同的行为。l 客户类(Client Class):负责创建Context类的实例,并负责创建算法的实现实例,然后赋给Context类实例的争取算法实现对象。7.2.2 作用当设计至代码维护时,为了服用目的而使用继承,结局并不完美。对父类的修改,会影响到子类型。在超类中增加的方法,会导致子类也有该方法,甚至练那些不该具备该方法的子类型也无法免除。7.2.3 适用范围这个模式的核心是把类的变化行为提取为一个接口,然后将类的变化部分用这个接口对象代替,也即声明为这个接口的一个属性,然后增加

15、一个设置这个接口的方法,以便以后修改这个类的具体行为,这样这个类可以不用管这个变化行为的具体实现,将实现委托给这个接口的具体实现。而关于这个接口的一系列实现,可以看 成是一族算法。这样就实现了将类中变化的部分封装,算法的使用跟算法的实现分离,算法被提取出来之后还可以更好的进行复用。在以下几种情况下通常会使用策略模式:l 当许多相关的类之间的差异只在于其行为时。策略模式可以动态地让一个对象在许多行为中选择一种行为。l 当实现一个目的有很多中可以选算法时,比如:您出于不同的利弊权衡考虑定义的那些算法(即相当于应用不同的策略)。这些具体的算法可以封装城抽象算法类的派生类,并享用该抽象算法类的统一接口

16、。通过多态性,客户端只要持有一个抽象算法类的对象,就可以选用任何一个具体的算法。l 当一个算法使用的数据不可以让客户端得知时。使用策略模式可以避免暴露复杂的算法相关的数据结构。其实客户端也没有必要知道这些算法相关的知识和数据。l 当一类定义有很多行为,切用多个条件语句来判断悬着这些行为时。策略模式可以把这些行为转移到对应的具体策略类中,从而避免了难以维护的多重条件选择,体现了面向对象的编程思想。7.2.4 实现通过以下步骤,开发人与啊可以很容易地在软件中实现策略模式:l 对策略对象定义一个公共接口。l 编写策略类,该类实现了上面的公共接口。l 在使用策略对象的类中保存一个对策略对象的引用。l

17、在使用策略对象的类中,实现对策略对象的set和get犯法。先要实现一个鸭子模拟器,这个鸭子模拟器由Duck及其子类描述,代码分别如下所示。Duck类:package com.sanlian.mode.strategy;/* * 鸭子 * author sanlian * version Nov 23, 2010 9:50:14 AM */public abstract class Duck /* * 叫行为 */public void quack()System.out.println(Quack);/* * 游泳 */public void swim()System.out.println(

18、All ducks float,even decoys.);/* * 外观 * 因为每种鸭子的外观个不相同,所以定义为抽象方法,由子类型自己完成 */public abstract void display();MallardDuck(野鸭)类:package com.sanlian.mode.strategy;/* * 野鸭 * author sanlian * version Nov 23, 2010 9:54:56 AM */public class MallardDuck extends Duck Overridepublic void display() /野鸭显示为绿头System

19、.out.println(Green head.);ReaHeadDuck(红头鸭)类:package com.sanlian.mode.strategy;/* * 红头鸭 * author sanlian * version Nov 23, 2010 9:56:40 AM */public class ReaHeadDuck extends Duck Overridepublic void display() /红头鸭显示为红头System.out.println(Red head.);RubberDuck(橡皮鸭)类:package com.sanlian.mode.strategy;/*

20、 * 橡皮鸭 * author sanlian * version Nov 23, 2010 9:57:07 AM */public class RubberDuck extends Duck /* * 橡皮鸭的叫声为吱吱叫,所以重写父类以改写行为 */public void quack()System.out.println(Squeak);Overridepublic void display() /橡皮鸭显示为黄头System.out.println(Yellow head.);上述代码,初始实现得非常好。现在我们如果要给Duck中加入fly()方法的话,那么在子类型中均有了该方法,于是

21、我们看到了会飞的橡皮鸭子,你看见过吗?当然,我们可以在子类中通过重写该方法以解决该方法对于子类型的影响。但是父类中再增加其它方法呢?通过继承在父类中提供行为,会导致如下缺点:l 代码在多个子类中重复。l 运行时的行为不容易改变。l 改变会牵一发动全身,造成部分子类型不想要的改变。以上面举的鸭子为例,您也许想到使用接口,将飞的行为、叫的行为定义为接口,然后让Duck的各种子类型实现这些接口。这时候代码如下:/* * 鸭子 * author sanlian * version Nov 23, 2010 9:50:14 AM */public abstract class Duck public v

22、oid swim()System.out.println(All ducks float,even decoys.);public abstract void display();/* * 野鸭 * author sanlian * version Nov 23, 2010 9:54:56 AM */public class MallardDuck extends Duck implements FlyBehavior,QuackBehavior public void quack()System.out.println(Quack);Overridepublic void display()

23、 System.out.println(Green head.);public void fly() System.out.println(Fly.);/* * 红头鸭 * author sanlian * version Nov 23, 2010 9:56:40 AM */public class ReaHeadDuck extends Duck implements FlyBehavior,QuackBehaviorOverridepublic void display() System.out.println(Red head.);public void fly() System.out

24、.println(Fly.);public void quack() System.out.println(Quack);/* * 橡皮鸭 * author sanlian * version Nov 23, 2010 9:57:07 AM */public class RubberDuck extends Duck implements QuackBehavior public void quack()System.out.println(Squeak);Overridepublic void display() System.out.println(Yellow head.);上述代码谁染

25、解决了一部分问题,让子类型可以有选额的提供一些行为(如fly()方法将不会出想在橡皮鸭中)。但我们也看到,野鸭子(MallardDuck类)和红头鸭(RedHeadDuck类)的一些相同行为代码不能得到重复使用。在一段程序之后,让我们从细节中跳出来,关注一些共性问题。不管使用什么语言,构建什么应用,在软件开发上,一直伴随着的不变的真理是:需要一直在变化。不管当初软件设计得多好,一段时间之后,总是需要成长与改变,否则软件就会死亡。我们知道,继承在某种程度上可以实现代码重用,但是父类(例如鸭子类Duck)的行为在子类型中是不断变化的,让所有子类型都有这些行为是不恰当的。我们可以将这些行为定义为接口

26、,让Duck的各种子类型去实现,但接口不具有实现代码,所以实现接口无法达到代码复用。这意味着,当我们需要修改某个行为,必须往下追踪并在每一个定义此行为的类中修改它,一不小心,会造成新的错误。设计原则:把应用中变化的地方独立出来,不要和那些不需要变化的代码混在一起。这样代码变化引起的不经意后果变少,系统变得更有弹性。按照上述设计原则,我们重新审视之前的Duck代码。1、分开变化的内容和不变的内容Duck类中的行为 fly()、quack(),每个子类型可能有自己特有的表现,这就是所谓的变化的内容。Duck类中的行为 swim() 每个子类型的表现均相同,这就是所谓不变的内容。我们将变化的内容从D

27、uck()类中剥离出来单独定义形成接口以及一系列的实现类型。将变化的内容定义形成接口可实现变化内容和不变内容的剥离。其实现类型可实现变化内容的重用。这些实现类并非Duck.java的子类型,而是专门的一组实现类,称之为行为类。由行为类而不是Duck.java的子类型来实现接口。这样,才能保证变化的行为独立于不变的内容。代码如下:/* * 飞行为接口 * author sanlian * version Nov 23, 2010 10:42:40 AM */public interface FlyBehavior /* * 飞方法 */void fly();/* * 变化的fly行为实现类之一

28、* author sanlian * version Nov 23, 2010 10:43:29 AM */public class FlyWithWings implements FlyBehavior public void fly() System.out.println(Im flying.);/* * 变化的fly行为实现类之二 * author sanlian * version Nov 23, 2010 10:43:29 AM */public class FlyNoWay implements FlyBehavior public void fly() System.out.p

29、rintln(I cont fly.);/* * 变化的quack行为接口 * author sanlian * version Nov 23, 2010 10:45:09 AM */public interface QuackBehavior /* * 叫方法 */void quack();/* * 变化的quack行为实现类之一 * author sanlian * version Nov 23, 2010 10:46:18 AM */public class Quack implements QuackBehavior public void quack() System.out.pri

30、ntln(Quack.);/* * 变化的quack行为实现类之二 * author sanlian * version Nov 23, 2010 10:47:11 AM */public class Squeak implements QuackBehavior public void quack() System.out.println(Squeak.);/* * 变化的quack行为实现类之三 * author sanlian * version Nov 23, 2010 10:47:57 AM */public class MuteQuack implements QuackBehav

31、ior public void quack() System.out.println();通过以上设计,fly()行为以及quack()行为已经和Duck.java没有什么关系,可以充分得到复用。而且我们很容易增加新的行为, 既不影响现有的行为,也不影响Duck.java。但是,大家可能有个疑问,就是在面向对象中行为不是体现为方法吗?为什么现在被定义形成类(例如Squeak.java)?在OO中,类代表的东西一般是既有状态(实例变量)又有方法。只是在本例中碰巧东西是个行为。既使是行为,也有属性及方法,例如飞行行为,也需要一些属性记录飞行的状态,如飞行高度、速度等。2、整合变化的内容和不变的内容

32、Duck.java将 fly()以及quack()的行为委拖给行为类处理。不变的内容如下:package com.sanlian.mode.strategy;/* * 鸭子类 * author sanlian * version Nov 23, 2010 10:49:58 AM */public abstract class Duck /将行为类声明为接口引用,降低对行为实现类型的依赖FlyBehavior flyB;QuackBehavior quackB;public void performFly()/不自行处理fly()行为,而是委托给引用的flyB所指向的行为对象this.flyB.

33、fly();public void performQuack()this.quackB.quack();public void swim()System.out.println(All ducks float, even decoys.);public abstract void display();Duck.java不关心如何进行fly()以及quack(),这些细节交由具体的行为类完成。package com.sanlian.mode.strategy;/* * 野鸭 * author sanlian * version Nov 23, 2010 11:15:16 AM */public

34、class MallardDuck extends Duck /最终可以省略该构造方法中的默认创建public MallardDuck()flyB = new FlyWithWings();quackB = new Quack();Overridepublic void display() System.out.println(Green head .);测试类:package com.sanlian.mode.strategy;/* * 测试类 * author sanlian * version Nov 23, 2010 11:14:43 AM */public class Test pu

35、blic static void main(String args) Duck duck = new MallardDuck();duck.performFly();duck.performQuack();运行上面测试类,其结果如图7-5所示。图7-5 运行结果在Duck.java子类型MallardDuck.java的构造方法中,直接实例化行为类型,在编译的时侯便指定具体行为类型。当然我们可以通过工厂模式或其它模式进一步解藕,或做到在运行时动态地改变行为。3、动态设定行为在父类Duck.java中增加设定行为类型的setter方法,接受行为类型对象的参数传入。为了降藕,行为参数被声明为接口类型。这样,既便在运行时,也可以通过调用这两个方法以改变行为。package com.sanlian.mode.strategy;/* * 鸭子类 * author sanlian * version Nov 23, 2010 10:49:58 AM */public abstract class Duck /相同部分省略.public void setFlyB(FlyBehavior

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论