前言

对于设计模式里创建型模式和结构型模式的学习完成了
现在开始行为型模式 - 模板方法模式的学习


行为型模式

思考行为型模式是什么?为什么要这么划分?

前面的创建型模式和结构型模式:

  • 创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”
  • 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配

行为型模式分为类行为模式对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为

行为型模式包括:模板方法模式、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式 11种设计模式

这些会在后来的学习中了解,现在先学习模板方法模式


现实中的问题

前面说了行为型模式是设计描述程序中复杂的流程控制

一个情景:

一个请客流程抽象成3个步骤:点单 - 》 吃东西 - 》买单

对于吃不同的东西,这个流程是一样的,只是第二个步骤:吃东西不一样

我们可以把这些规定了流程或格式的实例定义成模板,允许根据需求设计更改模板中,如吃东西可以吃面条,也可以修改成吃满汉全席


模板方法模式

模板方法模式(Template Method Pattern):定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中

在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式

模板方法模式结构:

模板方法模式的角色:
(1) 抽象类(AbstractClass):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。

① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

② 基本方法:是整个算法中的一个步骤,包含以下几种类型。

  • 抽象方法:在抽象类中申明,由具体子类实现。
  • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
  • 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

(2) 具体子类(ConcreteClass):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。

可以看出结构图中只有类之间的继承关系,没有对象关联关系

模板方法模式实现

使用模板方法模式实现上面的请客案例
其中需要定义一个抽象类:请客过程
定义两种具体的请客:请吃面条、请吃满汉全席

package com.company.Behavioral;
//抽象类:定义模板
abstract class Treat {//请客模板final  public void treatEat(){//请客的具体流程:点单 - 》 吃菜 - 》 买单order();eat();pay();}//具体流程:点单方法private void order(){System.out.println(" 服务员,点菜。。。");}//具体流程:抽象方法,由子类具体实现abstract void eat();//具体流程:买单方法private void pay(){System.out.println(" 这餐我买单。。。 ");}
}
//具体实现类:请吃面条
class TreatNoodles extends Treat{private String food = "noodles";@Overridevoid eat() {System.out.println(" 今天,我请大家吃 "+food+"。。。");}
}
//具体实现类:请吃满汉全席
class TreatFull extends Treat{private String food = "满汉全席";@Overridevoid eat() {System.out.println(" 今天,我请大家吃 "+food+"。。。");}
}class Client{public static void main(String[] args) {Treat treatNoodles = new TreatNoodles();treatNoodles.treatEat();}
}

模板方法模式很简单,主要就是通过抽象类给出模板的大致流程,再由子类去实现一下特定的方法


钩子方法扩展模板方法模式

钩子方法:默认不做任何事,子类可以根据情况选择是否重写这个方法,通过这个方法控制父类的流程

前面抽象类中定义好了具体的流程,如果对流程有一定的改动,可以使用钩子方法约束,通过在子类中实现的钩子方法对父类方法的执行进行约束,实现子类对父类行为的反向控制

例如:在吃饭之前加一个流程喝酒
就可以通过一个判断方法(钩子方法),决定是否喝酒

package com.company.Behavioral;
//抽象类:定义模板
abstract class Treat {//请客模板final public void treatEat(){//请客的具体流程:点单 - 》 吃菜 - 》喝酒- 》 买单order();//钩子方法决定是否喝酒if (isDrink()){drink();}eat();pay();}//具体流程:点单方法private void order(){System.out.println(" 服务员,点菜。。。");}//具体流程:喝酒private void drink(){System.out.println(" 来来来,干了这杯82年的老白干。。。");}//具体流程:抽象方法,由子类具体实现abstract void eat();//具体流程:买单方法private void pay(){System.out.println(" 这餐我买单。。。 ");}//钩子方法public boolean isDrink(){return true;}}
//具体实现类:请吃面条
class TreatNoodles extends Treat{private String food = "noodles";@Overridevoid eat() {System.out.println(" 今天,我请大家吃 "+food+"。。。");}@Overridepublic boolean isDrink(){return false;}
}
//具体实现类:请吃满汉全席
class TreatFull extends Treat{private String food = "满汉全席";@Overridevoid eat() {System.out.println(" 今天,我请大家吃 "+food+"。。。");}
}class Client{public static void main(String[] args) {//吃面条就别喝酒了Treat treatNoodles = new TreatNoodles();treatNoodles.treatEat();System.out.println("-------------------");//吃满汉全席一定要喝酒Treat treatFull = new TreatFull();treatFull.treatEat();}
}

钩子方法就这么简单


模板方法模式的优缺点

优点

  • 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。
  • 模板方法模式是一种代码复用的基本技术。
  • 模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”

缺点

  • 每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度
  • 由于是通过继承重写父类实现,违背了“里氏替换原则”,且因为是继承,违背了“合成复用原则”的思想

适用环境

以下场景适用模板方法模式:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复
  • 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现
  • 控制子类的扩展

具体使用

模板方法模式很简单,也很实用,这种模式频繁的使用与诸多框架中(Spring、Struts等)
只要复杂的算法中有公共部分,就可以使用模块方法模式,其实在实际编程中经常用到模块方法模式的思想

Spring IOC容器初始化时运用到的模板方法模式


模式扩展知识

  1. 继承

经常说里氏替换原则:继承不应该重写父类方法
合成复用原则:应该先考虑聚合、组合,再考虑继承

模板方法模式鼓励我们恰当使用继承,此模式可以用来改写一些拥有相同功能的相关类,将可复用的一般性的行为代码移到父类里面,而将特殊化的行为代码移到子类里面。这也进一步说明,虽然继承复用存在一些问题,但是在某些情况下还是可以给开发人员带来方便,模板方法模式就是体现继承优势的模式之一

  1. 好莱坞原则

好莱坞原则的定义为:“不要给我们打电话,我们会给你打电话(Don‘t call us, we’ll call you)”

在模板方法模式中,子类通过重写父类的方法来实现某些具体的业务逻辑
具体的流程是在父类中控制的,父类控制对子类的调用

好莱坞原则体现在:子类不需要调用父类,而通过父类来调用子类;由父类控制整个过程


模式细节

  1. 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
  2. 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  3. 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
  4. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
  5. 一般模板方法都加上final关键字, 防止子类重写模板方法.
  6. 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理

总结

  • 行为型模式有11中具体设计模式,主要是描述程序运行中复杂的流程控制
  • 模板方法模式是定义一个算法骨架、流程,具体在子类中实现某些特定的步骤
  • 模板方法模式有两个角色:抽象类:负责给出算法的骨架,由一个final模板方法和具体方法组成,具体方法可以定义钩子方法(用于子类控制父类流程),定义抽象方法(由子类完成的特定的步骤);具体实现类:实现一些具体步骤
  • 模板方法模式的优点是子类定义详细的处理算法时不会改变算法的结构,实现了代码的复用,通过对子类的扩展可以增加新的行为,符合“开闭原则”
  • 模板方法模式的缺点是每个不同的实现都定义一个子类,使得系统复杂、庞大,设计更抽象,且因为是继承重写实现,违背了里氏替换原则、合成复用原则;模板方法模式应当在特定场景下使用
  • 模板方法模式适用场景:算法复杂对算法的分割;有公共的行为提取集中到公共父类避免代码重复;控制子类的扩展
  • 在很多框架中都使用了模板方法模式,且我们实际编程中也或多或少的使用到了模板方法模式的思想

设计模式(16)行为型模式 - 模板方法模式相关推荐

  1. Java设计模式之行为型:模板方法模式

    一.什么是模板方法模式: 模板方法是基于继承实现的,在抽象父类中声明一个模板方法,并在模板方法中定义算法的执行步骤(即算法骨架).在模板方法模式中,可以将子类共性的部分放在父类中实现,而特性的部分延迟 ...

  2. 【设计模式】行为型02模板方法模式(Template Method Patten)

    五一长假,没有出去,不喜欢嘈杂的人群,玩了会游戏发泄了下憋在心底的戾气,手旁大马克杯里是母亲泡的绿茶.点开自己的播放列表,耳机里传来的是理查德克莱德曼的致爱丽丝.自己是个凡人,卑微渺小的活着.不说废话 ...

  3. Java设计模式之行为型:解释器模式

    一.什么是解释器模式:         解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例. 这里我们将语言理解成使用规定格式和语 ...

  4. Java设计模式之行为型:访问者模式

    背景: 去医院看病时,医生会给你一个处方单要你去拿药,拿药我们可以分为两步走: (1)去柜台交钱,划价人员会根据处方单上的药进行划价,交钱. (2)去药房拿药,药房工作者同样根据处方单给你相对应的药. ...

  5. Java设计模式之行为型:状态模式

    背景: 介绍状态模式前,我们先看这样一个实例:公司力排万难终于获得某个酒店的系统开发项目,并且最终落到了你的头上.下图是他们系统的主要工作: 当第一眼看到这个系统时你就看出这是一个状态图,每个框都代表 ...

  6. Java设计模式之行为型:备忘录模式

    在开发过程中,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,在编程时假如编写失误,例如不小心误删除了几行代码,我们希望返回删除前的状态,便可以使用 Ctrl+Z 来进行返回,这时 ...

  7. Java设计模式之行为型:命令模式

    前言: 在开发中,我们可能需要向某些对象发送一些请求,但我们不知道请求的具体接收者是谁,也不知道被请求的操作是哪个,只知道在系统运行中指定具体的请求接收者即可,打个比方,电视遥控器,我们只需知道按哪个 ...

  8. Java设计模式之行为型:迭代器模式

    一.什么是迭代器模式: 实际开发中,我们针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但我们不希望在集合容器的抽象接口层中充斥着各种不同的遍历操作,这时候我们就需要一种能完成下面功能的迭代器 ...

  9. Java设计模式之行为型:策略模式

    一.背景: 在开发中经常遇到这种情况,实现某个功能有多种算法策略,我们可以根据不同环境或者条件选择不同的算法策略来完成该功能,比如查找.排序等,一种常用方式是硬编码在一个类中,如需要提供多种查找算法, ...

  10. Java设计模式之创建型:原型模式

    一.什么是原型模式: 原型模式主要用于对象的创建,使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.UML类图如下: 原型模式的核心是就是原型类 Prototype,Prototype ...

最新文章

  1. 飞桨端到端开发套件揭秘:低成本开发的四大秘密武器
  2. java.lang.IllegalStateException: 启动子级时出错
  3. 第一百三十八期:37 个MySQL数据库小知识,为面试做准备
  4. Mybatis 系列2-配置文件
  5. java 方法保存变量_java – 在初始化对象或将其保存为变量并调用该方法时,调用方法的速度更快...
  6. 光敏电阻控制led亮度程序_单片机开发系统学习LED亮度控制原理
  7. 吴恩达《机器学习》第十三章:聚类
  8. office2013 应用程序无法正常启动(0xc000007b)
  9. 复制含有随机指针节点的链表
  10. 状态寄存器OF,CF,ZF,test,cmp的应用
  11. 儒雅计算机导师,龙桂鲁:儒雅的学者,严慈的良师-清华大学新闻网
  12. DNS错误不能上网怎么办?电脑dns错误修复方法?
  13. 计算机文档怎么字符加宽间距,Word2013设置字符间距,如何设置两个字符之间的距离 -电脑资料...
  14. 【读书笔记】吴军阅读与写作讲义
  15. 一根均线选股法_一根均线选股法视频教程
  16. Perl之Spreadsheet::WriteExcel
  17. iOS - CAEmitterlayer粒子发射器
  18. 使用AHK减少鼠标和方向键的使用频率,高效编辑
  19. 登录163邮箱续费情况怎么查询?163vip邮箱怎么收费?
  20. ACM之路的高一下学期

热门文章

  1. 基于胡桃夹子优化算法求解单目标优化问题附Matlab代码
  2. 将svn项目转到git上
  3. Ubuntu VNC 安装/使用/故障解决 看这一篇就够了
  4. 一些简单的递归练练。
  5. 求不黑,这样的广东高考作文能打几分?
  6. Zookeeper-实战
  7. 前端工程师--系统学习文档
  8. 掌握数据就能通晓未来:数据科学家何以成为公司的预言家?
  9. 小黑身份证挂失登报,见到了几年不见的黑天鹅,肚子吃坏了疼了几天,投资自己尝试健身,去平谷吃羊肉准备去保定找小老黑的leetcode之旅:220. 存在重复元素 III
  10. 0420-如何为CDH集成Active Directory的Kerberos认证