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

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 设计模式》
《设计模式:可复用面向对象软件的基础》
《研磨设计模式》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容