首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

设计模式 ——— 模板方法模式

TEMPLATE METHOD(模板方法) ———— 类行为型模式

意图

定义一个操作中的算法骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用性

  • 需要固定定义算法骨架,实现一个算法的不变的部分,并把可变的行为留给子类来实现的情况;
  • 各个子类中具有公共行为,应该抽取出来,集中在一个公共类中去实现,从而避免代码重复;
  • 需要控制子类扩展的情况。模板方法模式会在特定的点来调用“hook”操作,这样只允许在这些点进行扩展;

结构

模板方法模式结构图

  • AbstractClass(抽象类) 定义抽象的原语操作(primitive operation),具体的子类将重定义它们以实现一个算法的各步骤。 实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作。
  • ConcreteClass(具体类) 实现原语操作以完成算法中与特定子类相关的步骤。

认识模板方法模式

变与不变

程序设计的一个很重要的思考点就是“变与不变”,也就是分析程序中哪些功能是可变的,哪些功能是不变的,然后把不变的部分抽象出来,进行公共的实现,把变化的部分分离出去,用接口来封装隔离,或者是用抽象类来约束子类行为。

模板方法模式很好的体现了这一点。模板类实现的就是不变的方法和算法的骨架,而需要变化的地方,都通过抽象方法,把具体实现延迟到子类去了,而且还通过父类的定义来约束了子类的行为,从而使系统能有更好的复用性和扩展性。

好莱坞法则

什么是好莱坞法则呢?简单点说,就是“不要找我们,我们会联系你”。

模板方法模式很好的体现了这一点,做为父类的模板会在需要的时候,调用子类相应的方法,也就是由父类来找子类,而不是让子类来找父类。

这其实也是一种反向的控制结构,按照通常的思路,是子类找父类才对,也就是应该是子类来调用父类的方法,因为父类根本就不知道子类,而子类是知道父类的,但是在模板方法模式里面,是父类来找子类,所以是一种反向的控制结构。

对设计原则的体现

模板方法很好的体现了开闭原则和里氏替换原则。

首先从设计上,先分离变与不变,然后把不变的部分抽取出来,定义到父类里面,比如算法骨架,比如一些公共的、固定的实现等等。这些不变的部分被封闭起来,尽量不去修改它了,要扩展新的功能,那就使用子类来扩展,通过子类来实现可变化的步骤,对于这种新增功能的做法是开放的。

其次,能够实现统一的算法骨架,通过切换不同的具体实现来切换不同的功能,一个根本原因就是里氏替换原则,遵循这个原则,保证所有的子类实现的是同一个算法模板(为了防止子类改变模板方法中的算法,可以将模板方法声明为final),并能在使用模板的地方,根据需要,切换不同的具体实现。

模板方法模式的实现

模板方法调用下列类型的操作
  • 模板方法:就是定义算法骨架的方法 。
  • 具体的操作:在模板中直接实现某些步骤的方法,通常这些步骤的实现算法是固定的,而且是不怎么变化的,因此就可以当作公共功能实现在模板里面。如果不需提供给子类访问这些方法的话,还可以是private的。这样一来,子类的实现就相对简单些。如果是子类需要访问,可以把这些方法定义为protected final的,因为通常情况下,这些实现不能够被子类覆盖和改变了。
  • 具体的AbstractClass操作:在模板中实现某些公共功能,可以提供给子类使用,一般不是具体的算法步骤的实现,只是一些辅助的公共功能。
  • 原语操作:就是在模板中定义的抽象操作,通常是模板方法需要调用的操作,是必需的操作,而且在父类中还没有办法确定下来如何实现,需要子类来真正实现的方法。
  • 钩子操作:在模板中定义,并提供默认实现的操作。这些方法通常被视为可扩展的点,但不是必须的,子类可以有选择的覆盖这些方法,以提供新的实现来扩展功能。比如:模板方法中定义了5步操作,但是根据需要,某一种具体的实现只需要其中的1、2、3这几个步骤,因此它就只需要覆盖实现1、2、3这几个步骤对应的方法。那么4和5步骤对应的方法怎么办呢,由于有默认实现,那就不用管了。也就是说钩子操作是可以被扩展的点,但不是必须的。
  • Factory Method:在模板方法中,如果需要得到某些对象实例的话,可以考虑通过工厂方法模式来获取,把具体的构建对象的实现延迟到子类中去。
模板方法模式实现中指的注意的问题:

① 使用访问控制:必须重定义的原语操作须定义为abstract函数。模板方法自身不需要被重定义,并且也不应该被重定义,为了防止子类改变模板方法中的算法,可以将模板方法声明为final。 ② 尽量减少原语操作:定义模板方法的一个重要目的是尽量减少一个子类具体实现算法时必须重定义的那些原语操作的数目。需要重定义的操作越多,客户程序就越冗长。 ③ 命名约定:可以给应被重定义的那些操作的名字加上一个前缀以识别它们。

优缺点

优点: ①实现代码复用 模板方法模式是一种实现代码复用的很好的手段。通过把子类的公共功能提炼和抽取,把公共部分放到模板里面去实现。

缺点: ①算法骨架不容易升级 模板方法模式最基本的功能就是通过模板的制定,把算法骨架完全固定下来。事实上模板和子类是非常耦合的,如果要对模板中的算法骨架进行变更,可能就会要求所有相关的子类进行相应的变化。所以抽取算法骨架的时候要特别小心,尽量确保是不会变化的部分才放到模板中。

相关模式

  • 模板方法模式 VS 工厂方法模式 这两个模式可以配合使用。 模板方法模式可以通过工厂方法来获取需要调用的对象。
  • 模板方法模式 VS 策略模式 模板方法会定义一个算法的大纲,然后由子类通过继承来实现其中某些步骤的内容;策略模式则是通过对象组合的方式,让客户可以选择算法实现。即,使用委托来改变整个算法。 但是,我们可以在模板方法中使用策略模式,就是把那些变化的算法步骤通过使用策略模式来实现,但是具体选取哪个策略还是要由外部来确定,而整体的算法步骤,也就是算法骨架就由模板方法来定义了。
参考

《Head First 设计模式》 《设计模式:可复用面向对象软件的基础》 《研磨设计模式》

下一篇
举报
领券