敏捷开发笔记(第14章节)--TEMPLATE METHOD模式和STRATEGY模式:继承与委托
目录
1:PDF上传链接
14.1 TEMPLATE METHOD模式
14.1.1 滥用模式
14.2 STRATEGY模式
14.3结论
1:PDF上传链接
【免费】敏捷软件开发(原则模式与实践)资源-CSDN文库
“业精于勒”。
一中国谚语
早在20世纪90年代初期一也就是面向对象发展的初期一人们就非常看重继承这个概念。继承关系蕴涵的意义是非常深远的。使用继承我们可以基于差异编程(program by difference)!也就是说,对于某个满足了我们大部分需要的类,可以创建一Jerrilee M.Koheke个它的子类,并只改变其中我们不期望的部分。只是继承一个类,就可以重用该类的代码!通过继承,我们可以建立完整的软件结构分类,其中每一层都可以重用该层次以上的代码。这是一个美丽的新世界。
像大多数美丽新世界一样,它最终也被证明有些不切实际。直到1995年,人们才清楚地认识到继承非常容易被过度使用,而且过度使用的代价是非常高的。Gamma,,Helm,Johnson和Vlissides甚至强调,“优先使用对象组合(object composition)而不是类继承(class inheritance)。”①所以我们减少了对继承的使用,常常使用组合或者委托来代替它。
本章讲述了两个模式,并归纳了继承和委托之间的区别。TEMPLATE METHOD模式和STRATEGY模式所要解决的问题是类似的,而且常常可以互换使用。不过,TEMPLATE METHOD模式使用继承来解决问题,而STRATEGY模式使用的则是委托。
TEMPLATE METHOD模式和STRATEGY模式都可以分离通用的算法和具体的上下文。在软件设计中经常会看到这样的需求。我们有一个通用的算法。为了遵循依赖倒置原则(DP),我们想确保这个通用的算法不要依赖于具体的实现。我们更想使这个通用的算法和具体的实现都依赖于抽象。
14.1 TEMPLATE METHOD模式
Initialize();
while(!done()) //main loop
{Idle(); //do something useful.
}Cleanup();
首先进行初始化。接着进入主循环。在主循环中完成需要做的工作,这些工作或许是处理GUI事件,或许是处理数据库记录。最后,一旦完成了工作,程序就退出主循环,并且在程序终止前做些清除工作。
这种结构非常常见,所以可以把它封装在一个名为Application的类中。之后我们就可以在每个想要编写的新程序中重用这个类。想想!我们再也不需要去编写这个循环了!
例如,在程序14.1中,我们看到了这种标准程序的所有组成部分。其中,初始化了InputStreamReader和BufferedReader,.并且有-一个主循环从BufferedReader中读取华氏温度,并把该温度转换成摄氏温度打印出来。最后,打印出一条退出信息。
程序14.1 ftoc rawimport java.io.*;
public class ftocraw
{public static void main(String[] args) throws Exception{InputstreamReader isr new InputStreamReader(System.in);BufferedReader br new BufferedReader(isr);boolean done = false;while (!done){String fahrstring br.readLine();if (fahrstring == null || fahrString.length() == 0)done true;else{double fahr = Double.parseDouble(fahrString);double celcius = 5.0 /9.0 * (fahr - 32);System.out.printin("F=" + fahr + "C=" + celcius);}}System.out.println("ftoc exit");}
}
这个程序完全符合主循环结构。它先做一些初始化,接着在主循环中完成要做的工作,最后做一些清理工作并退出。
我们可以应用TEMPLATE METHOD模式把这个基本结构从oc程序中分离出来。该模式把所有通用代码放入一个抽象基类(abstract base class)的实现方法中。这个实现方法完成了这个通用算法,但是将所有的实现细节都交付给该基类的抽象方法。
这样,例如,我们可以把这个主循环结构封装在一个名为Application的抽象基类中。(参见程序14.2。)
程序14.2 Application.javapublic abstract class Application
{private boolean isDone = false;protected abstract void init ()protected abstract void idle();protected abstract void cleanup ();protected void setDone ()(isDone true;}protected boolean done ()(return isDone;}public void run (){ init();while (!done ())idle();cleanup ();}
}
该类描绘了一个通用的主循环应用程序。从实现的u函数中,可以看到主循环。也可以看到
所有的工作都被交付给抽象方法int、idle以及cleanup.。init方法处理任何所需的初始化工作;idle方法处理程序的主要工作,并且在setDone方法被调用之前被重复调用:cleanup方法处理程序退出前所需的所有清理工作。
我们可以通过继承Application来重写toc类,只需要实现Application中的抽象方法即可。程序14.3展示了重写后的程序。
程序14.3 ftocTemplateMethod.javaimport java.io.*
public class ftocTemplateMethod extends Application
{private InputStreamReader isr;private BufferedReader br;public static void main(String[] args)throws Exception{(new ftocTemplateMethod()).run();}protected void init(){isr = new InputstreamReader (System.in);br = new BufferedReader (isr);}protected void idle(){String fahrstring = readLineAndReturnNullIfError ()if (fahrString == null || fahrString.length ()== 0)setDone ();else{double fahr = Double.parseDouble(fahrstring);double celcius = 5.0 / 9.0 *(fahr-32);System.out.println("F=" + fahr + ", C=" + celcius);}}protected void cleanup(){System.out.println("ftoc exit");}private String readLineAndReturnNullIfError(){String s;try{s = br.readLine();}catch(IOException e){s = null;}return s;}
}
由于进行了一些异常处理,程序显得略微长了些,但是还是能很容易地看出原先的o心应用程序是如何适配到TEMPLATE METHOD模式上去的。
14.1.1 滥用模式
此时,你应该考虑这样的问题,“他是认真的吗?他真希望我在所有的新应用中都使用这个Application类吗?它没有带来任何有价值的东西,只是使问题复杂化了。”我之所以选择这个例子,是因为它简单,并且为展示TEMPLATE METHOD模式的机制提供了一个良好的平台。另一方面,我确实不推荐用这样的方法来构建c。
这是滥用模式的一个好例子。在这个特定的应用程序中,使用TEMPLATE METHOD模式是荒谬的。它使程序变得复杂庞大。把每个应用程序的主循环以一种通用的方式封装起来,一开始这听起来很好,但是本例中的实际应用结果却是无益的。
设计模式是很好的东西。它们可以帮助解决很多设计问题。但是它们的存在并不意味着必须要经常使用它们。本例中,虽然可以应用TEMPLATE METHOD模式,但是使用它是不明智的,因为使用该模式的代价要高于它所带来的好处。
TEMPLATE METHOD模式展示了面向对象编程中诸多经典重用形式中的一种。其中通用算法被放置在基类中,并且通过继承在不同的具体上下文中实现该通用算法。但是这项技术是有代价的。继承是一种非常强的关系。派生类不可避免地要和它们的基类绑定在一起。
14.2 STRATEGY模式
STRATEGY模式使用了一种非常不同的方法来倒置通用算法和具体实现之间的依赖关系。再来考虑一下滥用模式的Application问题。
不是将通用的应用算法放进一个抽象基类中,而是将它放进一个名为ApplicationRunner的具体类中。我们把通用算法必须要调用的抽象方法定义在一个名为Application的接口中。我们从这个接口派生出ftocStrategy,并把它传给ApplicationRunner。之后,ApplicationRunner就可以把具体工作委托给这个接口去完成。
STRATEGY模式比TEMPLATE METHOD模式多提供了一个额外的好处。尽管TEMPLATE METHOD模式允许一个通用算法操纵多个可能的具体实现,但是由于STRATEGY模式完全遵循DP原则,从而允许每个具体实现都可以被多个不同的通用算法操纵。
14.3结论
TEMPLATE METHOD模式和STRATEGY模式都可以用来分离高层的算法和低层的具体实现细节。都允许高层的算法独立于它的具体实现细节重用。此外,STRATEGY模式也允许具体实现细节独立于高层的算法重用,不过要以一些额外的复杂性、内存以及运行时间开销作为代价。