模板方法模式PPT推荐.pptx
《模板方法模式PPT推荐.pptx》由会员分享,可在线阅读,更多相关《模板方法模式PPT推荐.pptx(28页珍藏版)》请在冰点文库上搜索。
,创建咖啡,4,publicclassCoffeevoidprepareRecipe()boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
publicvoidboilWater(),System.out.println(Boilingwater);
publicvoidbrewCoffeeGrinds()System.out.println(DrippingCoffeethroughfilter);
publicvoidpourInCup()System.out.println(Pouringintocup);
publicvoidaddSugarAndMilk()System.out.println(AddingSugarandMilk);
每个方法都实现算法中的一个步骤:
煮沸水、冲泡咖啡、把咖啡倒进杯子、加糖和牛奶。
创建茶,5,publicclassTea,voidprepareRecipe()boilWater();
steepTeaBag();
addLemon();
publicvoidsteepTeaBag()System.out.println(Steepingthetea);
publicvoidaddLemon()System.out.println(AddingLemon);
泡茶专有的两个方法。
第二步和第四步与咖啡的实现不同,其它的实现一样。
第一版设计,6,caffeineBeverageprepareRecipe()boilWater()pourInCup(),CoffeeprepareRecipe()brewCoffeeGrinds()addSugarAndMilk(),TeaprepareRecipe()steepTeaBag()addLemon(),咖啡和茶特有的方法放在子类中。
每个子类都实现自己的冲泡法。
每个子类都覆盖prepareRecipe()方法,并实现自己的冲泡法。
prepareRecipe()方法在每个类中都不一样,所以定义成抽象的方法。
boilWater()和pourInCup()方法被两个子类所共享,所以被定义在超类中。
能否改进这个设计?
7,星巴兹咖啡和茶冲泡法分析,星巴兹咖啡和茶的冲泡法采用了相同的算法:
把水煮沸用热水泡咖啡或茶把饮料倒进杯子在饮料内加入适当的调料第一步和第三步完全相同已经被抽出来,放到基类了。
第二步和第四步的抽象也是相同的。
抽象prepareRecipe(),8,我们遇到的第一个问题就是:
茶使用steepTeaBag()和addLemon()方法,而咖啡使用brewCoffeeGrinds(),和addSugarAndMilk()方法。
咖啡voidprepareRecipe()boilWater();
茶voidprepareRecipe()boilWater();
9,抽象prepareRecipe()(续),无论是咖啡的冲泡,还是茶的浸泡,都是用沸水泡,我们给它一个新的方法名称,比如说brew()。
同样,无论是咖啡加糖和牛奶,还是茶加柠檬,都是加调料,我们也给它一个新的方法名称addCondiments()。
这样,新的prepareRecipe()方法看起来就象这样:
voidprepareRecipe()boilWater();
brew();
addCondiments();
抽象prepareRecipe()(续),10,/抽象类,CaffeineBeverage(咖啡因饮料)超类:
publicabstractclassCaffeineBeveragefinalvoidprepareRecipe()boilWater();
abstractvoidbrew();
abstractvoidaddCondiments();
voidboilWater(),System.out.println(Boilingwater);
voidpourInCup()System.out.println(Pouringintocup);
prepareRecipe()声明为final,是不让子类覆盖这个方法!
步骤2和步骤4被泛化为brew()和addCondiments()。
这两个方法声明为抽象的,是因为咖啡和茶的做法不同,需要子类实现。
11,抽象prepareRecipe()(续),咖啡和茶都依赖于超类(咖啡因饮料)处理冲泡法。
publicclassCoffeeextendsCaffeineBeveragepublicvoidbrew()System.out.println(DrippingCoffeethroughfilter);
publicvoidaddCondiments()System.out.println(AddingSugarandMilk);
publicclassTeaextendsCaffeineBeveragepublicvoidbrew()System.out.println(Steepingthetea);
publicvoidaddCondiments()System.out.println(AddingLemon);
用沸水浸泡茶叶加柠檬,12,用沸,水冲泡咖啡,加糖和牛奶,我们茶做了把水煮沸什么?
用沸水浸泡茶叶把茶到进杯子加柠檬,咖啡把水煮沸用沸水冲泡咖啡粉把咖啡到进杯子加糖和牛奶,咖啡因饮料把水煮沸冲泡把饮料到进杯子加调料,泛化,泛化,茶子类,咖啡子类,咖啡因饮料了解和控制冲泡法步骤,亲自执行步骤1和步骤3,但需要茶和咖啡完成步骤2和步骤4。
一些步骤需要子类进行,一些步骤需要子类进行,两种冲泡法只是一些步骤的实现不同,所以我们泛化了冲泡法,把它放在基类。
13,会会模板方法,刚刚实现的就是模板方法模式。
模板方法定义了一个算法步骤,并允许子类为一个或多个步骤提供实现。
我们再看看咖啡因饮料类的结构(下页)。
publicabstractclassCaffeineBeveragevoidfinalprepareRecipe()boilWater();
voidboilWater()/实现voidpourInCup()/实现,14,prepareRecipe()是模板方法:
因为:
它是一个方法。
它用作一个算法的模板。
在这个例子中,算法是用来制作咖啡因饮料的。
这个模板中,算法内每个步骤都被一个方法代表了。
某些方法是由超类处理的。
某些方法是由子类处理的。
需要子类提供的方法,在超类中声明为抽象。
会会模板方法(续),boilWater();
15,brew();
prepareRecipe()方法控制了算法,不能改变它。
这个方法也会依赖子类来提供某些或所有步骤的实现。
模板方法如何工作(以泡茶为例),首先需要一个茶对象:
Tea=myTea=newTea();
然后调用这个模板方法:
myTea.prepareRecipe();
依照这算法制作咖啡因饮料把水煮沸:
boilWater();
/是在超类中进行的泡茶,这件事只有子类知道怎么做:
把茶到进杯子里:
/是在超类中进行的最后,加进调料。
由于调料是各个饮料独有的,所以由子类来实现它:
Tea,CaffeineBeverageprepareRecipe()boilWater()pourInCup(),brew()addCondiments(),模板方法带来的好处?
模板方法模式定义,16,模板方法模式在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
AbstractClasstemplateMethod()primitiveOperation1()primitiveOperation2(),17,ConcreteClassprimitiveOperation1()primitiveOperation2(),primitiveOperation1()primitiveOperation2(),抽象类包含了模板方法。
板方法用,抽,到象的版本,的操作的。
模,个具体,可能有多个都实,类,每一方法所,需的全部,现了模板操作。
具体类实现了抽象的操作,当模板方法需要这两个抽象方法时,就会调用它们。
模板方法在实现算法过程中,用到了这两个操作。
模板方法本身和这两个操作的具体实现之间被解耦了。
模板方法模式类图,钩子(hook),18,钩子是一种被声明为抽象类的方法,但只有空的或默认的实现。
有了钩子,可以让子类有能力对算法的不同点进行挂钩。
要不要挂钩,由子类自行决定。
abstractclassAbstractClass/细节忽略了voidhook(),这是一个抽象类中不做任何事情的具体方法,即钩子(hook)。
有了钩子,我能够决定要不要覆盖方法。
如果我不提供自己的方法,抽象类会提供一个默认的实现。
19,对模板方法挂钩,publicabstractclassCaffeineBeverageWithHookvoidprepareRecipe(),boilWater();
if(customerWantsCondiments()addCondiments();
对模板方法挂钩,20,voidboilWater()System.out.println(Boilingwater);
voidpourInCup()System.out.println(Pouringintocup);
booleancustomerWantsCondiments()returntrue;
这就是一个钩子,,子类可以覆盖这个方法,但不一定这么做。
21,使用钩子,为了使用钩子,我们在子类中覆盖它。
在这里,钩子控制咖啡因饮料是否执行某部分算法。
或更确切地说是在饮料中要不要加进调料。
publicclassCoffeeWithHookextendsCaffeineBeverageWithHookpublicvoidbrew()System.out.println(DrippingCoffeethroughfilter);
22,privateStringgetUserInput()Stringanswer=null;
System.out.print(Wouldyoulikemilkandsugarwithyourcoffee(y/n)?
);
BufferedReaderin=newBufferedReader(newInputStreamReader(System.in);
tryanswer=in.readLine();
catch(IOExceptionioe)System.err.println(IOerrortryingtoreadyouranswer);
if(answer=null)returnno;
returnanswer;
publicbooleancustomerWantsCondiments()Stringanswer=getUserInput();
if(answer.toLowerCase().startsWith(y)returntrue;
elsereturnfalse;
覆盖了钩子。
让用户输入对调料的决定。
通过命令行获得用户的输入。
23,执行测试程序,publicclassBeverageTestDrivepublicstaticvoidmain(Stringargs)TeaWithHookteaHook=newTeaWithHook();
CoffeeWithHookcoffeeHook=newCoffeeWithHook();
System.out.println(nMakingtea.);
teaHook.prepareRecipe();
System.out.println(nMakingcoffee.);
coffeeHook.prepareRecipe();
测试结果,24,加柠檬。
不加糖和牛奶。
好莱坞原则,25,好莱坞原则别调用(打电话给)我们,我们会调用(打电话给)你。
好莱坞原则可以防止“依赖腐败”。
当高层组件依赖低层组件,低层组件又依赖高层组件,高层组件又依赖边侧组件,边侧组件又依赖高层组件,依赖腐败就发生了。
在这种情况下,没有人可以轻易搞懂系统是如何设计的。
好莱坞原则(续),26,高层组件,低层低层组件组件,以,参与计算低,层组件可。
低层组,不可以件绝对,用高层直接调组件。
在好莱坞原则下,允许低层组件挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。
即高层组件对低层组件的方式是“别调用我们,我们会调你”。
件控制何高层组如何让低,层组件,时以及参与。
CaffeineBeverageprepareRecipe()boilWater()pourInCup()brew()addCondiments(),27,Coffeebrew()addCondiments(),Teabrew()addCondiments(),这些子类只简单用来提供一些实现细节。
T,ea,和,Co,有,果不,没会,直,先接,被调,调用,用抽,,象,绝类,ffee如对。
们,erage是我控,ffeineBev,它能够有,的某个冲泡法的实现,Ca只高层组件算法,方,法,时,才调在,制类。
需要子类用子,饮料的客,赖Caffein户代码只,象,而不eBeverage依,Tea或Cof依赖具体抽,减少整个fee,这可的,系统的依以,赖。
好莱坞原则与模板方法,28,其它模板方法实例,考虑一个计算存款利息的例子。
假设系统需要支持两种存款账号,两种账号的存款利息是不同的,因此,在计算一个存户的存款利息额时,必须区分两种不同的账号类型。
由于利息计算涉及到两个步骤:
一是确定账户的类型,二是确定利息的百分比,因此系统需要两个基本方法:
一个基本方法给出账号种类,另一个基本方法给出利息百分比。
这两个基本方法构成具体逻辑,因为账号的类型不同,所以具体逻辑会有所不同。
显然,系统需要一个抽象角色给出顶级行为的实现,而将两个作为细节步骤的基本方法留给具体子类实现。