分析设计模式之模板方法Java实现


Posted in Java/Android onJune 23, 2021
目录
  • 一、什么是模板方法设计模式
    • 1.1、模板方法的用途
    • 1.2、模板方法的定义
  • 二、定义模板方法的步骤
    • 2.1、定义模板类
    • 2.2、定义具体子类
    • 2.3、定义客户端调用
    • 2.4 下抽象类和子类之间的UML图和源码实现
  • 三、案例
    • 3.1、案例1: 一日规划
    • 3.2、案例2: 钩子方法
  • 四、模板方法的优缺点
    • 4.1、优点
    • 4.2、缺点
  • 五、使用场景
  • 六、对设计模式六大原则的应用思考

 

一、什么是模板方法设计模式

从字面意义上理解, 模板方法就是定义出来一套方法, 作为模板, 也就是基础。 在这个基础上, 我们可以进行加工,实现个性化的实现。比如:一日餐三. 早餐, 中餐, 晚餐. 每个人都要吃三餐, 但每个人的三餐吃的可能都不一样. 一日三餐定义了模板--早中晚, 每个人的三餐就是模板的具体实现.

 

1.1、模板方法的用途

  • 将不变的行为从子类搬到超类,去除了子类中的重复代码
  • 规范子类的结构

 

1.2、模板方法的定义

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

 

二、定义模板方法的步骤

 

2.1、定义模板类

通常模板类是抽象类,负责给出算法的轮廓或者框架。他是有若干个模板方法和若干个基本方法构成。

1.模板方法

定义了算法的骨架, 定义了方法调用的顺序, 其中包含一个或者多个基本方法

2.基本方法

基本算法有三种类型:

​a) 抽象方法:子类必须重写的方法。没有默认实现。

​b)具体方法:父类定义的默认实现,有实现逻辑,可以被具体的子类继承或重写

​c)钩子方法:判断的逻辑方法和需要子类重写的空方法两种。

 

2.2、定义具体子类

具体子类,也就是具体的实现类, 实现抽象类中的抽象方法。他们是抽象的模板方法中一个组成部分。

 

2.3、定义客户端调用

客户端调用抽象类, 实例化的时候实例化具体类, 只需要调用抽象类的模板方法就可以了。

 

2.4 下抽象类和子类之间的UML图和源码实现

1.UML图

分析设计模式之模板方法Java实现

从图中可以看出抽象类的结构可以定义三类方法。 可以有一个也可以有多个。子类必须需要实现抽象类中的抽象方法,可以选择性重写父类的具体方法。子类实现接口的时候,要多思考设计模式的六大原则。

2.源码

先定义抽象类, 也就是框架。

package com.lxl.www.designPatterns.templatePattern.template;

/**
 * 抽象类, 定义模板
 */
public abstract class AbstractClass {

    /**
     * 定义模板方法
     * 规范了流程的框架
     */
    public void templateMethod() {
        // 先调用具体方法
        specificMethod();
        // 在调用抽象方法
        abstractMethod();
    }

    /**
     * 具体方法
     */
    public void specificMethod() {
        // 具体的公共逻辑, 父子类通用
        System.out.println("具体方法---父子类通用逻辑");
    }

    /**
     * 抽象方法
     *
     * 抽象方法, 子类必须重写
     */
    public abstract void abstractMethod();

}

在定义具体的实现类, 实现父类的抽象方法

package com.lxl.www.designPatterns.templatePattern.template;

/**
 * 具体实现类
 */
public class ConcreteClass extends AbstractClass{

    /**
     * 重写父类的抽象方法
     */
    @Override
    public void abstractMethod() {
        System.out.println("具体实现类--重写父类的抽象方法");
    }
}

最后定义客户端调用

package com.lxl.www.designPatterns.templatePattern.template;

/**
 * 模板方法客户端
 */
public class TemplateClient {
    public static void main(String[] args) {
        AbstractClass abstractClass = new ConcreteClass();
        abstractClass.templateMethod();
    }
}

运行结果:

具体方法---父子类通用逻辑

具体实现类--重写父类的抽象方法

对照模板方法设计模式,我们来看一个具体的案例。

 

三、案例

 

3.1、案例1: 一日规划

每个人的一日安排都有三餐, 早餐, 中餐,晚参。 但每个人的三餐食物不尽相同,我们来看看每个人的三餐变化, 以及三餐前后要做的事情。

package com.lxl.www.designPatterns.templatePattern.oneDayArrangement;

/**
 * 一日三餐抽象类
 */
public abstract class ArrangementAbstract {

    /**
     * 模板方法
     * 规定了一天的框架
     */
    public void templateMethod() {
        System.out.println("一日安排如下: ");
        getUp();
        breakfast();
        lunch();
        dinner();
        getDown();
    }

    public void getUp() {
        System.out.println("起床");
    }

    public void getDown() {
        System.out.println("睡觉");
    }

    /**
     * 早餐抽象类
     */
    public abstract void breakfast() ;

    /**
     * 午餐抽象类
     */
    public abstract void lunch();

    /**
     * 晚餐抽象类
     */
    public abstract void dinner();

}

定义一日三餐抽象类。每个人的日程安排都是,起床,早餐,中餐,晚餐,睡觉。 其中起床和睡觉是每个人都要做的事情,三餐也是,但三餐的食物不同,于是我们将三餐定义为抽象

一日安排实现类

package com.lxl.www.designPatterns.templatePattern.oneDayArrangement;

/**
 * 张三的一日三餐安排
 */
public class PersonArrangement extends ArrangementAbstract{
    private String name;
    public PersonArrangement(String name) {
        this.name = name;
    }

    /**
     * 早餐抽象类
     */
    public void breakfast(){
        System.out.println(name + "--早餐吃牛奶面包");
    }

    /**
     * 午餐抽象类
     */
    public void lunch() {
        System.out.println(name + "--中餐吃食堂");
    }

    /**
     * 晚餐抽象类
     */
    public void dinner() {
        System.out.println(name + "--晚餐吃水果");
    }

}

客户端调用

public class Client {
    public static void main(String[] args) {
        ArrangementAbstract zhangsan = new PersonArrangement("张三");
        zhangsan.templateMethod();
    }
}

运行结果:

一日安排如下:
起床
张三--早餐吃牛奶面包
张三--中餐吃食堂
张三--晚餐吃水果
睡觉

可以看出, 完全按照模板方法的步骤实现。

 

3.2、案例2: 钩子方法

我们上面说了, 模板方法设计模式中, 基本方法包括抽象方法,具体方法和钩子方法.
如果能够使用好钩子方法, 可以在程序中完美实现子类控制父类的行为. 我们来看下面的案例:

我们在抽象方法中定义一个钩子方法hookMethod(), 在模板方法templateMethod()中,钩子方法控制了代码的流程.

UML图:

分析设计模式之模板方法Java实现

源代码:

package com.lxl.www.designPatterns.templatePattern.hookMethod;

/**
 * 抽象类, 定义模板
 */
public abstract class AbstractClass {

    /**
     * 定义模板方法
     * 规范了流程的框架
     */
    public void templateMethod() {
        // 调用具体方法
        specificMethod();
        // 钩子方法控制下一步骤
        if (hookMethod()) {
            // 调用抽象方法
            abstractMethod();
        }
    }

    /**
     * 具体方法
     */
    public void specificMethod() {
        // 具体的公共逻辑, 父子类通用
        System.out.println("具体方法---父子类通用逻辑");
    }

    /**
     * 钩子方法
     * 钩子方法是有具体实现的,
     */
    public boolean hookMethod() {
        return true;
    }

    /**
     * 抽象方法
     *
     * 抽象方法, 子类必须重写
     */
    public abstract void abstractMethod();

}

定义具体实现

/**
 * 具体实现类
 */
public class ConcreteClass extends AbstractClass {

    /**
     * 重写父类的抽象方法
     */
    @Override
    public void abstractMethod() {
        System.out.println("具体实现类--重写父类的抽象方法");
    }

    /**
     * 钩子方法
     * @return
     */
    @Override
    public boolean hookMethod() {
        System.out.println("重写了父类的钩子方法, 反向控制父类的行为");
        return false;
    }
}

重写了钩子方法, 反向控制父类的行为

public class TemplateClient {
    public static void main(String[] args) {
        AbstractClass abstractClass = new ConcreteClass();
        abstractClass.templateMethod();
    }
}

运行结果

具体方法---父子类通用逻辑
重写了父类的钩子方法, 反向控制父类的行为

如果子类钩子方法 HookMethod() 的代码改变,则程序的运行结果也会发生改变。

 

四、模板方法的优缺点

 

4.1、优点

1.规范了框架, 封装了不变的部分, 扩展了可变的部分. 父类定义框架, 并抽象了公共不变的部分, 子类通过重写扩展完善了框架的实现。

2.使用了"开闭原则", 对扩展开放, 对修改关闭. 子类可以通过重写父类的抽象方法来扩展父类的实现。

3.行为集中有父类控制, 规范流程。

 

4.2、缺点

1.每一种实现都需要定义一个具体实现类, 增加类的数量, 系统更加复杂。

2.继承的缺点, 一旦父类增加一个抽象方法, 所有子类都需要增加. 这一点违背"开闭原则"。

3.父类中的抽象方法由子类实现, 子类的执行结果影响父类, 这种"反向控制"结构, 会增加代码的复杂性。

 

五、使用场景

1.算法的整体步骤是固定的,但个别部分容易发生变化时,可以考虑使用模板方法设计模式,将容易发生变化的部分抽象出来,提供给子类去实现。

2.当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

3.当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

4.重构时,模板方法模式是一个经常使用到的模式,把相同的代码抽取到父类中,通过钩子函数约束其行为

 

六、对设计模式六大原则的应用思考

1.单一职责原则: 一个方法只有一个引起变化的原因, 这个不太好看出, 要开子类代码的具体实现

2.里式替换原则: 父类出现的地方都可以使用子类替换,并且结果保持一致. 子类重写了父类的方法。 模板方法设计模式可能违背里式替换原则, 不过,这正是能够“反向控制”的原理

3.接口隔离原则: 依赖于最小的单一接口, 而不是胖接口. 符合

4.依赖倒置原则: 依赖于抽象, 而不是依赖于具体. 符合

5.迪米特法则: 最少知识原则. 之和朋友沟通, 减少和朋友的沟通. 这个需要看子类具体实现是否符合

6.开闭原则: 违背开闭原则, 一旦父类增加一个抽象方法, 所有子类都需要对应增加

以上就是分析设计模式之模板方法的详细内容,更多关于设计模式 模板方法的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
总结一些Java常用的加密算法
Jun 11 Java/Android
新手入门Jvm-- JVM对象创建与内存分配机制
Jun 18 Java/Android
springboot集成flyway自动创表的详细配置
Jun 26 Java/Android
JavaGUI模仿QQ聊天功能完整版
Jul 04 Java/Android
mybatis 获取无数据的字段不显示的问题
Jul 15 Java/Android
关于ObjectUtils.isEmpty() 和 null 的区别
Feb 28 Java/Android
Java9新特性对HTTP2协议支持与非阻塞HTTP API
Mar 16 Java/Android
详解Spring Security中的HttpBasic登录验证模式
Mar 17 Java/Android
Netty客户端接入流程NioSocketChannel创建解析
Mar 25 Java/Android
Spring Boot 底层原理基础深度解析
Apr 03 Java/Android
Java虚拟机内存结构及编码实战分享
Apr 07 Java/Android
Spring Cloud OpenFeign模版化客户端
Jun 25 Java/Android
基于Java的MathML转图片的方法(示例代码)
Jun 23 #Java/Android
springboot如何初始化执行sql语句
Java循环队列与非循环队列的区别总结
Jun 22 #Java/Android
springBoot基于webSocket实现扫码登录
Jun 22 #Java/Android
SpringBoot集成Redis,并自定义对象序列化操作
Java如何实现树的同构?
启动Tomcat时出现大量乱码的解决方法
You might like
《雄兵连》系列首部大电影《烈阳天道》:可能是因为期望值太高了
2020/08/18 国漫
磨咖啡豆的密诀
2021/03/03 冲泡冲煮
屏蔽浏览器缓存另类方法
2006/10/09 PHP
PHP has encountered an Access Violation at 7C94BD02解决方法
2009/08/24 PHP
使用dump函数,给php加断点测试
2013/06/25 PHP
yii实现使用CUploadedFile上传文件的方法
2015/12/28 PHP
Smarty模板引擎缓存机制详解
2016/05/23 PHP
Thinkphp连表查询及数据导出方法示例
2016/10/15 PHP
php函数mkdir实现递归创建层级目录
2016/10/27 PHP
Yii2框架视图(View)操作及Layout的使用方法分析
2019/05/27 PHP
laravel框架实现敏感词汇过滤功能示例
2020/02/15 PHP
Jquery ajax不能解析json对象,报Invalid JSON错误的原因和解决方法
2010/03/27 Javascript
JavaScript 获取当前时间戳的代码
2010/08/05 Javascript
JavaScript中的console.dir()函数介绍
2014/12/29 Javascript
jquery获取checkbox的值并post提交
2015/01/14 Javascript
微信小程序 switch组件详解及简单实例
2017/01/10 Javascript
使用get方式提交表单在地址栏里面不显示提交信息
2017/02/21 Javascript
使用JavaScript进行表单校验功能
2017/08/01 Javascript
js定时器+简单的动画效果实例
2017/11/10 Javascript
vue实现某元素吸顶或固定位置显示(监听滚动事件)
2017/12/13 Javascript
vue.js使用v-model实现表单元素(input) 双向数据绑定功能示例
2019/03/08 Javascript
JavaScript中callee和caller的区别与用法实例分析
2019/06/28 Javascript
[42:32]完美世界DOTA2联赛循环赛 Magma vs PXG BO2第二场 10.28
2020/10/28 DOTA
Python语言编写电脑时间自动同步小工具
2013/03/08 Python
python使用xmlrpc实例讲解
2013/12/17 Python
零基础写python爬虫之神器正则表达式
2014/11/06 Python
Python的Django框架中的表单处理示例
2015/07/17 Python
基于python3 类的属性、方法、封装、继承实例讲解
2017/09/19 Python
python实现字符串加密 生成唯一固定长度字符串
2019/03/22 Python
小米旗下精品生活电商平台:小米有品
2018/12/18 全球购物
经典C++面试题一
2016/11/06 面试题
介绍一下Python中webbrowser的用法
2013/05/07 面试题
秋季红领巾广播稿
2014/01/27 职场文书
开展读书活动总结
2014/06/30 职场文书
高中数学教学反思范文
2016/02/18 职场文书
Mysql 如何批量插入数据
2021/04/06 MySQL