当前位置: 首页 > article >正文

Java设计模式之模板方法模式

目录

定义

结构

案例

优缺点

优点

缺点

使用场景

JDK源码解析

无法查看的无参read()原因


定义

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。简单来说,就是一些事情的步骤一样,只有具体业务实现不相同,所以将具体业务的实现放在子类中实现。

结构

模板方法模式包含以下主要角色:

  • 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
    • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
      • 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。
      • 钩子方法 :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
      • 具体方法 :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
  • 具体子类:实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

案例

比如说炒不同的菜整体步骤相同,但是具体操作不同。

创建抽象类

//抽象类中包括基本方法以及模板方法
public abstract class AbstractClass {
    //模板方法
    public final void temp(){
        step1();
        step2();
        step3();
        step4();
        step5();
    }

    //基本方法
    public void step1(){
        System.out.println("进行步骤1");
    }

    public void step2(){
        System.out.println("进行步骤2");
    }

    public abstract void step3();

    public abstract void step4();

    public void step5(){
        System.out.println("进行步骤5");
    }
}

不同业务的具体步骤实现 

public class A extends AbstractClass {
    @Override
    public void step3() {
        System.out.println("A业务进行了步骤3");
    }

    @Override
    public void step4() {
        System.out.println("A业务进行了步骤4");
    }
}

public class B extends AbstractClass {
    @Override
    public void step3() {
        System.out.println("B业务进行了步骤3");
    }

    @Override
    public void step4() {
        System.out.println("B业务进行了步骤4");
    }
}

测试 

public class Client {
    public static void main(String[] args) {
        A a = new A();
        a.temp();
        System.out.println("==========");
        B b = new B();
        b.temp();
    }
}

运行结果如下 

进行步骤1

进行步骤2

A业务进行了步骤3

A业务进行了步骤4

进行步骤5

==========

进行步骤1

进行步骤2

B业务进行了步骤3

B业务进行了步骤4

进行步骤5模板方法需要使用final修饰来防止子类重写修改模板方法实现流程。

优缺点

优点

  • 提高代码复用性将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。
  • 实现了反向控制通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。

缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

使用场景

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。涉及到钩子函数

JDK源码解析

InputStream使用到了模板方法模式,在InputStream中存在三个read()方法

	//使用了abstract修饰,子类必须重写
	public abstract int read() throws IOException;
	//一个参数的read方式实际上调用的参数为三个的read方法
    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方法,每次读取一个字节
        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()在哪里实现了这个读取字符功能。

FileInputStream继承了InputStream,重写了read()方法,执行的是read0()方法,但是无法查看read0()方法的实现代码。

抽象方法是无参的read()方法,模板方法是三个参数的read()方法。通过调用无参的read方法实现一次读取多个字节

无法查看的无参read()原因

read0()方法使用了native关键词修饰,代表调用的是本地方法,所谓本地方法就是非java语言实现的方法,所以无法查看read0()实现源码。


http://www.kler.cn/a/107117.html

相关文章:

  • Springboot配置全局异常通用返回
  • SQL面试题——奔驰SQL面试题 车辆在不同驾驶模式下的时间
  • 基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解
  • 比ChatGPT更酷的AI工具
  • 响应式网页设计--html
  • React的基础API介绍(二)
  • 体育竞技分析
  • 服务器数据恢复-服务器系统损坏启动蓝屏的数据恢复案例
  • C++11的lambda表达式
  • 使用docker安装MySQL,Redis,Nacos,Consul教程
  • 【软考系统架构设计师】2020年系统架构师综合知识真题及解析
  • 什么是解构赋值?
  • macOS 12 Monterey v12.7.1正式版:开启全新的操作系统体验
  • 2023 MathorCup(妈妈杯) 数学建模挑战赛B题完整解题思路+模型+代码
  • Android11系统桌面隐藏指定APP图标
  • ab压力测试
  • 基于springboot实现校园疫情防控系统项目【项目源码+论文说明】
  • 【Python机器学习】零基础掌握BaggingClassifier集成学习
  • 爬虫、数据清洗和分析
  • User CSS 在性能优化方面的实践
  • JVM详解(InsCode AI 创作助手)
  • 第14期 | GPTSecurity周报
  • docker容器怎么设置开机启动
  • 线程状态,BLOCKED和WAITING 有什么区别
  • Ubuntu部署docker及docker常用操作
  • 计算机网络重点概念整理-第五章 传输层【期末复习|考研复习】