设计模式(十七)----行为型模式之模板方法模式

news2025/1/21 3:02:54

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

行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式分为:

  • 模板方法模式

  • 策略模式

  • 命令模式

  • 职责链模式

  • 状态模式

  • 观察者模式

  • 中介者模式

  • 迭代器模式

  • 访问者模式

  • 备忘录模式

  • 解释器模式

以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。

1 模板方法模式

1.1 概述

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

定义:

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

1.2 结构

模板方法(Template Method)模式包含以下主要角色:

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

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

    • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

      • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

      • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

      • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

        一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

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

1.3 案例实现

【例】炒菜

炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:

代码如下:

//抽象类(定义模板方法和基本方法)
public abstract class AbstractClass {
    // 模板方法
    public final void cookProcess() {
        //第一步:倒油
        this.pourOil();
        //第二步:热油
        this.heatOil();
        //第三步:倒蔬菜
        this.pourVegetable();
        //第四步:倒调味料
        this.pourSauce();
        //第五步:翻炒
        this.fry();
    }
​
    public void pourOil() {
        System.out.println("倒油");
    }
​
    //第二步:热油是一样的,所以直接实现
    public void heatOil() {
        System.out.println("热油");
    }
​
    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
    public abstract void pourVegetable();
​
    //第四步:倒调味料是不一样
    public abstract void pourSauce();
​
​
    //第五步:翻炒是一样的,所以直接实现
    public void fry(){
        System.out.println("炒啊炒啊炒到熟啊");
    }
}
​
public class ConcreteClass_BaoCai extends AbstractClass {
​
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }
​
    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}
​
public class ConcreteClass_CaiXin extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }
​
    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}
​
public class Client {
    public static void main(String[] args) {
        //炒手撕包菜
        ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
        baoCai.cookProcess();
        System.out.println("==============================");
        //炒蒜蓉菜心
        ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
        caiXin.cookProcess();
    }
}

注意:为防止恶意操作,一般模板方法都加上 final 关键词。

1.4 优缺点

优点:

  • 提高代码复用性

    将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。

  • 实现了反向控制

    通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。

缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。

  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

1.5 适用场景

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。

  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

1.6 JDK源码解析

InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read() 方法,如下:

// 抽象类
public abstract class InputStream implements Closeable {
    //抽象方法,要求子类必须重写
    public abstract int read() throws IOException;
​
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
​
    // 模板方法 定义算法的框架
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
​
        //调用了无参的read方法,该方法是每次读取一个字节数据
        // 其根本是调用子类的read方法,因为read必须被子类重写
        // 实现子类对父类的反向控制,这就是模板方法的核心思想
        int c = read(); 
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;
​
        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
}

从上面代码可以看到,无参的 read() 方法是抽象方法,要求子类必须实现。而 read(byte b[]) 方法调用了 read(byte b[], int off, int len) 方法,所以在此处重点看的方法是带三个参数的方法。

在该方法中第18行、27行,可以看到调用了无参的抽象的 read() 方法。

总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/397634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二、JVM内存结构

文章目录运行时数据区子系统程序计数器&#xff08;PC寄存器&#xff09;虚拟机栈本地方法接口和本地方法栈堆运行时数据区子系统 1、Java虚拟机定义了若干程序运行期间使用到的运行时数据区&#xff0c;其中有一些会随着虚拟机启动而创建&#xff0c;随着虚拟机推出而销毁。另…

SpringBoot接口 - 如何对参数进行校验

在以SpringBoot开发Restful接口时, 对于接口的查询参数后台也是要进行校验的&#xff0c;同时还需要给出校验的返回信息放到上文我们统一封装的结构中。那么如何优雅的进行参数的统一校验呢&#xff1f;什么是不优雅的参数校验后端对前端传过来的参数也是需要进行校验的&#x…

刷题笔记7 | 454.四数相加II 、383. 赎金信 、 15. 三数之和 、 18. 四数之和 、 总结

454.四数相加II 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < n nums1[i] nums2[j] nums3[k] nums4[l] 0 输入&#xff1a;nums1 [1,2], nums…

全网最全Fiddler抓包教程,超过十万人学习此教程!

零基础玩转Fiddler抓包在测试领域应用实战&#xff01;各位做测试的同学想必对抓包工具fiddler并不陌生&#xff0c;但是很多同学可能没有总结过它的用法&#xff0c;下面我总结了fiddler一些常用的用法。 Web端抓包配置 打开Fiddler&#xff0c;Tools -> Fiddler Options…

界面组件DevExpress WinForms v22.2 - 升级对HTML CSS的支持

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

3.Elasticsearch初步进阶

3.Elasticsearch初步进阶[toc]1.文档批量操作批量获取文档数据批量获取文档数据是通过_mget的API来实现的在URL中不指定index和type请求方式:GET请求地址:_mget功能说明:可以通过ID批量获取不同index和type的数据请求参数docs:文档数组参数_index:指定index_type:指定type_id:指…

分析vmlinux,uImage,zImage,Image的生成以及之间的关系

1.在内核源码顶层目录下打开Makefile&#xff0c;搜索vmlinux&#xff0c;找到以下内容&#xff1a;可知主要靠$(vmlinux-deps)生成。继续搜索查看变量vmlinux-deps的值&#xff0c;找到&#xff1a;KBUILD_LDS为链接文件&#xff0c;剩下两个变量如下&#xff1a;搜索head-y未…

网吧电脑怎么录屏?分享3种便捷方法,一键录屏

​网吧作为一个公共场所&#xff0c;为用户提供了方便快捷的上网服务。然而&#xff0c;在使用网吧电脑的过程中&#xff0c;有时候我们会需要录制屏幕内容&#xff0c;比如游戏的精彩瞬间&#xff0c;教学演示视频等。那么&#xff0c;网吧电脑怎么录屏呢&#xff1f;本文将为…

Linux应用学习——多进程

进程 当一个进程正常终止时可以通过int atexit(void(*function)(void))注册进程终止处理函数 PART1——进程相关概念 ​ 进程是一个动态的过程&#xff0c;而非一个静态的文件&#xff0c;是程序的一次运行过程&#xff0c;当应用程序被加载到内存中运行之后才能称为一个进程…

【LeetCode——排序链表】

文章目录排序链表二、解题思路&#xff1a;二.实现的代码总结&#xff1a;排序链表 一道链表排序题&#xff0c;链接在这里 二、解题思路&#xff1a; 解题思路&#xff1a;使用归并排序&#xff08;用递归实现&#xff09; 第一步&#xff1a;先找到链表的中间节点 第二步…

百分之九十的人不知道的退税秘诀

为什么会有退税和补税&#xff1f; 公司在帮我们进行个人所得税申报的时候&#xff0c;个人所得税是按月或者按季度预扣预缴的&#xff0c;税款会在每个月或季度的收入中扣除一定的比例。在年度个人所得税汇缴清算的时候&#xff0c;如果实际年度收入与预扣预缴的税款不符&…

【Spring 深入学习】配置DI 以及IOC的多种方式

配置DI 以及IOC的多种方式 1. 概述 防止将来遗忘&#xff0c;记录下DI等配置方式。从xml 配置文件 以及注解的方式来配置下DI 2. XML 方式 2.1 通过bean 依赖注入 xml 配置 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http:/…

Zeek安装、使用与压力测试

Zeek安装与压力测试Zeek安装、简单使用与压力测试环境Zeek安装zeek简单运行安装PF_RING修改Zeek配置文件&#xff0c;使用PF_RING&#xff0c;实现集群流量压力测试查看zeek日志Zeek安装、简单使用与压力测试 科研需要&#xff0c;涉及到Zeek的安装、使用和重放流量压力测试评…

【牛客刷题专栏】0x0F:JZ7 重建二叉树(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录前言问题…

VKL060 SSOP24超低功耗/抗干扰15*4 LCD液晶段码驱动IC,适用于传感器/数字压力表

产品型号&#xff1a;VKL060产品品牌&#xff1a;永嘉微电/VINKA封装形式&#xff1a;SSOP24产品年份&#xff1a;新年份原厂&#xff0c;工程服务&#xff0c;技术支持&#xff01;VKL060概述:VKL060是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大60点&#xff08;15…

【ROS学习笔记13】ROS中的TF坐标变换

【ROS学习笔记13】ROS中的TF坐标变换 文章目录【ROS学习笔记13】ROS中的TF坐标变换前言1. 静态坐标变换2. 动态坐标变换3. 多坐标变换4. 坐标系关系查看5. TF坐标变换实操Reference写在前面&#xff0c;本系列笔记参考的是AutoLabor的教程&#xff0c;具体项目地址在 这里 前言…

sqli-labs靶场实战

sqli-labs靶场实战 &#xff08;手工&#xff09;SQL注入基本步骤&#xff1a; 第一步&#xff1a;注入点测试 第二步&#xff1a;查询字段数 第三步&#xff1a;判断回显位 第四步&#xff1a;查询数据库的基本信息 第五步&#xff1a;爆数据库名 第六步&#xff1a;爆数据库…

ROS云课使用CoCubeSim案例

源码压缩包&#xff1a;https://gitcode.net/ZhangRelay/cocubesim打开蓝桥ROS云课&#xff1a;下载cocubesim压缩包&#xff1a;https://gitcode.net/ZhangRelay/cocubesim.git注意文件路径&#xff0c;home文件夹下的code文件夹里面。解压缩&#xff1a;tar -xf cocubesim/co…

黑马Linux笔记03【su、sudo、groupadd、useradd、usermod、userdel、getent、chmod、chown】

资源 视频地址&#xff1a;黑马-新版Linux快速入门到精通资源下载&#xff1a;https://pan.baidu.com/s/1zExrsk09QVm3mpqaPTqe_g?pwd6666&#xff0c;提取码&#xff1a;6666笔记 黑马Linux笔记01【安装VMware Workstation、安装CentOS7、远程连接Linux系统、Win10配置WSL(Ub…