使用Java8改造模板方法模式

news2024/12/23 23:43:17

目录

前言 

以前的模板方法

Java 8 的函数式编程

Java 8以后的模板方法

总结


前言 

我们在日常开发中,经常会遇到类似的场景:当要做一件事儿的时候,这件事儿的步骤是固定好的,但是每一个步骤的具体实现方式是不一定的。

通常,遇到这种情况,我们会把所有要做的事儿抽象到一个抽象类中,并在该类中定义一个模板方法。这就是所谓的模板方法模式。

以前的模板方法

当我们去银行的营业厅办理业务需要以下步骤:1.取号、2.办业务、3.评价。

三个步骤中取号和评价都是固定的流程,每个人要做的事儿都是一样的。但是办业务这个步骤根据每个人要办的事情不同所以需要有不同的实现。

我们可以将整个办业务这件事儿封装成一个抽象类:

public abstract class AbstractBusinessHandler {

    /**

     * 模板方法

     */

    public final void execute(){

        getNumber();

        handle();

        judge();

    }

    /**

     * 取号

     * @return

     */

    private void getNumber(){

        System.out.println("number-00" + RandomUtils.nextInt());

    }

    /**

     * 办理业务

     */

    public abstract void handle(); //抽象的办理业务方法,由子类实现

    /**

     * 评价

     */

    private void judge(){

        System.out.println("give a praised");

    }

}

我们在类中定义了一个execute类,这个类编排了getNumber、handle和judge三个方法。这就是一个模板方法

其中getNumber和judge都有通用的实现,只有handle方法是个抽象的,需要子类根据实际要办的业务的内容去重写。

有了这个抽象类和模板方法,当我们想要实现一个"存钱业务"的时候,只需要继承该AbstractBusinessHandeler并且重写handle方法即可:

public class SaveMoneyHandler extends AbstractBusinessHandeler {

    @Override
    public void handle() {

        System.out.println("save 1000");

    }

}

这样,我们在执行存钱的业务逻辑的时候,只需要调用 SaveMoneyHandler的execute方法即可:

public static void main(String []args){

    SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();

    saveMoneyHandler.execute();

}

输出结果:

number-00958442164

save 1000

give a praised

以上,就是一个简单的模板方法的实现。通过使用模板方法,可以帮助我们很大程度的复用代码。

因为我们要在银行办理很多业务,所以可能需要定义很多的实现类:

//取钱业务的实现类

public class DrawMoneyHandler extends AbstractBusinessHandeler {

    @Override

    public void handle() {

        System.out.println("draw 1000");

    }

}


//理财业务的实现类

public class MoneyManageHandler extends AbstractBusinessHandeler{

    @Override

    public void handle() {

        System.out.println("money manage");

    }

}

一直以来,开发者们在使用模板方法的时候基本都是像上面这个例子一样:需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。

但是,有了Java 8以后,模板方法有了另外一种实现方式,不需要定义特别多的实现类了。

Java 8 的函数式编程

2014年,Oracle发布了 Java 8,在Java 8中最大的新特性就是提供了对函数式编程的支持。

Java 8在java.util.function下面增加增加一系列的函数接口。其中主要有Consumer、Supplier、Predicate、Function等。

本文主要想要介绍一下Supplier和Consumer这两个,使用者两个接口,可以帮我们很好的改造模板方法。这里只是简单介绍下他们的用法,并不会深入展开,如果大家想要学习更多用法,可以自行google一下。

Supplier

Supplier是一个供给型的接口,简单点说,这就是一个返回某些值的方法。

最简单的一个Supplier就是下面这段代码:

public List<String> getList() {

    return new ArrayList();

}

使用Supplier表示就是:

Supplier<List<String>> listSupplier = ArrayList::new;

Consumer

Consumer 接口消费型接口,简单点说,这就是一个使用某些值(如方法参数)并对其进行操作的方法。

最简单的一个Consumer就是下面这段代码:

public void sum(String a1) {

    System.out.println(a1);

}

使用Consumer表示就是:

Consumer<String> printConsumer = a1 -> System.out.println(a1);

Consumer的用法,最见的的例子就是是Stream.forEach(Consumer)这样的用法,

它接受一个Consumer,该Consumer消费正在迭代的流中的元素,并对每个元素执行一些操作,比如打印:

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());

Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);

Java 8以后的模板方法

在介绍过了Java 8中的Consumer、Supplier之后,我们来看下怎么改造之前我们介绍过的模板方法。

首先,我们定义一个BankBusinessHandler类,并且重新定义一个execute方法,这个方法有一个入参,是Consumer类型的,然后移除handle方法,重新编排后的模板方法内容如下:


public class BankBusinessHandler {

    private void execute(Consumer<BigDecimal> consumer) {

        getNumber();

        consumer.accept(null);

        judge();

    }

    private void getNumber() {

        System.out.println("number-00" + RandomUtils.nextInt());

    }

    private void judge() {

        System.out.println("give a praised");

    }

}

我们实现的模板方法execute中,编排了getNumber、judge以及consumer.accept,这里面consumer.accept就是具体的业务逻辑,可能是存钱、取钱、理财等。需要由其他方法调用execute的时候传入。

这时候,我们想要实现"存钱"业务的时候,需要BankBusinessHandler类中增加以下方法:


public class BankBusinessHandler {

    public void save(BigDecimal amount) {

        execute(a -> System.out.println("save " + amount));

    }

}

在save方法中,调用execute方法,并且在入参处传入一个实现了"存钱"的业务逻辑的Comsumer。

这样,我们在执行存钱的业务逻辑的时候,只需要调用 BankBusinessHandler的save方法即可:

public static void main(String[] args) throws {

    BankBusinessHandler businessHandler = new BankBusinessHandler();

    businessHandler.save(new BigDecimal("1000"));

}

输出结果:

number-001736151440

save1000

give a praised

如上,当我们想要实现取钱、理财等业务逻辑的时候,和存钱类似:


public class BankBusinessHandler {

    public void save(BigDecimal amount) {

        execute(a -> System.out.println("save " + amount));

    }

    public void draw(BigDecimal amount) {

        execute(a -> System.out.println("draw " + amount));

    }

    public void moneyManage(BigDecimal amount) {

        execute(a -> System.out.println("draw " + amount));

    }

}

可以看到,通过使用Java 8中的Comsumer,我们把模板方法改造了,改造之后不再需要抽象类、抽象方法,也不再需要为每一个业务都创建一个实现类了。我们可以把所有的业务逻辑内聚在同一个业务类中。这样非常方便这段代码的后期运维。

前面介绍如何使用Consumer进行改造模板方法,那么Supplier有什么用呢?

我们的例子中,在取号、办业务、评价这三个步骤中,办业务是需要根据业务情况进行定制的,所以,我们在模板方法中,把办业务这个作为扩展点开放给外部。

有这样一种情况,那就是现在我们办业务的时候,取号的方式也不一样,可能是到银行网点取号、在网上取号或者银行客户经理预约的无需取号等。

无论取号的方式如何,最终结果都是取一个号;而取到的号的种类不同,可能接收到的具体服务也不同,比如vip号会到VIP柜台办理业务等。

想要实现这样的业务逻辑,就需要使用到Supplier,Supplier是一个"供给者",他可以用来定制"取号逻辑"。

首先,我们需要改造下模板方法:

public class BankBusinessHandler {
    public void execute(Supplier<String> supplier, Consumer<BigDecimal> consumer) {
        String number = supplier.get();
        System.out.println(number);
        if (number.startsWith("vip")) {
            //Vip号分配到VIP柜台
            System.out.println("Assign To Vip Counter");
        } else if (number.startsWith("reservation")) {
            //预约号分配到专属客户经理
            System.out.println("Assign To Exclusive Customer Manager");
        } else {
            //默认分配到普通柜台
            System.out.println("Assign To Usual Manager");
        }
        consumer.accept(null);
        judge();
    }

    public void getNumber() {
        System.out.println("number-00" + System.currentTimeMillis());
    }

    public void judge() {
        System.out.println("give a praised");
    }

}
/**
 * 当我们想要实现取钱、理财等业务逻辑的时候,和存钱类似:
 */
public class BusinessAbstraction extends BankBusinessHandler{

    public void saveVip(BigDecimal amount) {
        execute(() -> "vipNumber-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
    }
    public void save(BigDecimal amount) {
        execute(() -> "number-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
    }
    public void saveReservation(BigDecimal amount) {
        execute(() -> "reservationNumber-00" + System.currentTimeMillis(), a -> System.out.println("save " + amount));
    }


}
public class Main {
    public static void main(String[] args) {
        System.out.println("模版设计方法实践");
        BusinessAbstraction businessHandler = new BusinessAbstraction();
        businessHandler.saveVip(new BigDecimal("1000"));
    }
}

总结

以上,我们介绍了什么是模板方法模式,以及如何使用Comsumer和Supplier改造模板方法模式。

这样的做法是我们日常开发中经常会用到的,其实,我觉得本文中的例子并不是完完全全能表达出来我想表达的意思,但是我们的真实业务中的逻辑讲起来又比较复杂。

所以,这就需要大家能够多多理解并且实践一下。如果你代码中用到过模板方法模式,那一定是可以通过本文中的方法进行改造的。

如果你还没用过模板方法模式,那说明你的应用中一定有很多重复代码,那就赶紧用起来。

作为一个开发工程师,我们要尽最大努力的消灭应用中的重复代码,功在当代,利在千秋!

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

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

相关文章

Hudi(14):Hudi集成Flink之核心参数设置

目录 0. 相关文章链接 1. 去重参数 2. 并发参数 2.1. 参数说明 2.2. 案例演示 3. 压缩参数 3.1. 参数说明 3.2. 案例演示 4. 文件大小 4.1. 参数说明 4.2. 案例演示 5. Hadoop 参数 Flink可配参数官网地址&#xff1a;All Configurations | Apache Hudi 0. 相关文…

Ubuntu 18.04 安装 nvidia 显卡驱动 离线安装 禁用 nouveau

Ubuntu 18.04 安装 nvidia 显卡驱动 离线安装1 系统2 查看显卡2.1 更新 pci.ids 文件3 安装显卡驱动 510.543.1 安装 nvtop4 禁用 nouveau5 安装 cuda 11.6.15.1 设置环境变量1 系统 # lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubu…

bpflock:基于eBPF实现的Linux设备安全审计工具

关于bpflock bpflock是一款基于 eBPF驱动的Linux设备安全审计工具&#xff0c;该工具使用了eBPF来帮助广大研究人员增强Linux设备的安全性。通过限制对各种Linux功能的访问&#xff0c;bpflock能够减少攻击面并阻止一些众所周知的攻击技术。 bpflock只允许类似容器管理器、sy…

区间一维dp史上最细总结(听了绝对会了,还不会的一定要进来)

目录 那年初夏&#xff08;三&#xff09; 引入 1.动态规划是什么&#xff1f; 2.什么是区间动态规划问题&#xff1f; 定义 性质 3.为何总是要问这种问题&#xff1f; 区间动态规划基本 思考 步骤&#xff08;划重点&#xff09; 例题精讲 1.最长上升子序列 题目描…

8个 数据库性能优化方案,你知道几个?(建议收藏)

毫不夸张的说咱们后端工程师&#xff0c;无论在哪家公司&#xff0c;呆在哪个团队&#xff0c;做哪个系统&#xff0c;遇到的第一个让人头疼的问题绝对是数据库性能问题。如果我们有一套成熟的方法论&#xff0c;能让大家快速、准确的去选择出合适的优化方案&#xff0c;我相信…

IB数学AA/AI应该如何选择?

IB数学怎么选课&#xff1f;AA&#xff0c;AI&#xff0c;SL&#xff0c;HL适合哪些学生&#xff1f;如何学习&#xff1f;IB数学&#xff1a;AA与AI&#xff0c;到底应该怎么选&#xff1f;IB数学AA有多难&#xff1f;要不要学数学AA HL&#xff1f;适合学生 IB数学AA AA HL偏…

【SpringCloud复习巩固】Feign

目录 一.HTTP客户端Feign 1.1RestTemplate方式调用存在的问题 1.2Feign的介绍 1.3Feign的使用 1.4自定义Feign的配置 1.4.1配置Feign日志的两种方式 1.5Feign性能优化 1.5.1Feign的性能优化-连接池配置 1.6Feign的最佳实践 一.HTTP客户端Feign 1.1RestTemplate方式调用…

自学软件测试,现在年薪30w,我骄傲了吗?

从小老一辈的人就经常说&#xff0c;小时候不好好读书&#xff0c;长大了只能去工地搬砖。我是从小都不爱读书的人&#xff0c;但在上学时期我一直有一种优越感&#xff0c;认为自己读书很有天赋&#xff0c;读书就是比别人厉害&#xff0c;但事实证明也确实如此&#xff0c;高…

[Android Studio]Android Studio Logcat日志样式设置

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…

c++IO流!!!开工了!!!

1.什么是IO流 流是若干个字节组成的字节序列&#xff0c;简单来说指的是就是数据从一端到另一端 键盘到程序——>标准输入流程序到屏幕——>标准输出流程序到文件——>文件流 流类体系&#xff1a;一些体系管理输入和输出的流的操作 输入流输出流文件流 ios类 istream…

【DockerCE】使用docker配置和运行HertzBeat

HertzBeat是一款免Agent的监控平台&#xff0c;拥有强大自定义监控能力&#xff0c;可以对应用服务、中间件、数据库、操作系统、云原生等进行监控&#xff0c;配置监控告警阈值&#xff0c;以及告警通知(邮件、微信、钉钉、飞书)。关于这个软件的介绍&#xff0c;我这里就不做…

困扰多年的Docker和iptables的恩怨,今天解决了

先介绍下我的使用环境&#xff1a; 操作系统&#xff1a;CentOS7.9 Docker版本&#xff1a;20.10.21 事情是这样的&#xff0c;安装完Docker的时候&#xff0c;容器镜像都跑起来了&#xff0c;端口也放行了&#xff0c;就是无法控制系统防火墙friewalld,查看firewalld状态报错 …

【微信小游戏开发笔记】第一节:微信小游戏Cocos开发环境配置

微信小游戏开发环境配置 微信小游戏开发前&#xff0c;首先要做一些准备&#xff1a; 注册 微信公众平台 账号&#xff0c;获取小游戏AppID(小程序ID)。安装 微信开发者工具&#xff0c;用于编译小游戏。安装 Visual Studio Code&#xff0c;用于编写游戏逻辑代码。安装并配置…

Linux命令:wget(下载文件)、ssh(登录及免密登录)、scp(远程文件传输)、sh(脚本)

wget 概述 wget是一个下载文件的工具&#xff0c;用在命令行下&#xff0c;下载一些软件或从远程服务器恢复备份到本地服务器 wget支持HTTP&#xff0c;HTTPS和FTP协议&#xff0c;可以使用HTTP代理 支持自动下载 wget可以在用户退出系统的之后在后台执行。意味着你可以登…

C语言选择排序和快速排序(图解过程)+思路清晰

选择排序和快排选择排序时间复杂度和空间复杂度快排&#xff08;三种方式&#xff09;1.hoar时间复杂度和空间复杂度优化--三数取中优化--小区间优化2.挖坑法3.双指针&#xff08;推荐&#xff09;选择排序 本篇文章的重点在快排。因为选择排序无论是在思想上面还是&#xff0…

提供数百万岗位和丰厚利润,苹果却转移产业链,中国制造怎么办?

新年刚过&#xff0c;就传出消息指苹果直接代中国供应链企业向印度提出建厂申请&#xff0c;其中有14家获得了许可&#xff0c;而3家被否决&#xff0c;这凸显出苹果坚定向印度转移生产线&#xff0c;如此做对中国制造将产生深远影响。一、苹果对中国制造的影响巨大苹果为中国提…

新的一年,这份高级测试人的职业素养请收好~

软件测试工程师需要的专业技能计算机领域的专业技能是测试工程师应该必备的一项素质&#xff0c;是做好测试工作的前提条件。尽管没有任何IT背景的人也可以从事测试工作&#xff0c;但是一名要想获得更大发展空间或者持久竞争力的测试工程师&#xff0c;则计算机专业技能是必不…

5G R16+C-V2X赋能下一代智能T-Box,助力智能驾驶时代加速到来

█ 5G技术助力C-V2X持续进化&#xff0c;智能网联新生态逐步建立 汽车行业正面临百年未有之变局&#xff0c;智能汽车已经成为全球汽车产业发展的战略方向。发改委、工信部、交通部等11部委联合印发的《智能汽车创新发展战略》中指出&#xff1a;汽车产业与相关产业全面融合&a…

(二十四)深入理解蓝牙BLE之“H5协议”

前言&#xff1a;蓝牙产品在实际落地中&#xff0c;很多时候采用hostcontroller的通信模型&#xff0c;其中host负责实现协议栈profile是运行在主控cpu上的。controller为另外一颗单独的蓝牙芯片&#xff0c;负责蓝牙link layer的处理&#xff0c;两个芯片通过hci消息来交互数据…

SD卡提示格式化后怎么办?可尝试这种数据恢复方法快速找回!

生活中&#xff0c;虽然我们更习惯用手机、U盘来存储数据&#xff0c;但是对于摄影爱好者&#xff0c;SD卡还是非常刚需。 在使用SD卡存储文件时&#xff0c;经常遇到SD卡无法读取&#xff0c;要求我们格式化后才可以使用。此时&#xff0c;该如何备份里面的数据&#xff0c;或…