使用Java8优化模板方法模式

news2024/12/22 20:21:27

目录

前言 

以前的模板方法

Java 8 的函数式编程

Java 8以后的模板方法

总结


前言 

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

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

以前的模板方法

当我们平常点外卖的时候需要以下步骤:1.外卖平台、2.点外卖、3.评价。

三个步骤中外卖平台和评价都是固定的流程,每个人要做的事基本都一样。但是点外卖这个步骤根据每个人喜欢吃的爱好而选择不同。

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

public abstract class AbstractBusinessHandler {

    /**

     * 模板方法

     */

    public final void execute(){

        getDeliveryPlatform();

        chooseFood();

        judge();

    }

    /**

     * 打开外卖平台

     * @return

     */

    private void getDeliveryPlatform(){

        System.out.println("打开了美团");

    }

    /**

     * 点外卖

     */

    public abstract void chooseFood(); //抽象的的美食方法,由子类实现

    /**

     * 评价

     */

    private void judge(){

        System.out.println("我点的外卖太好吃了");

    }

}

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

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

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

public class TaxiBusiness extends AbstractBusinessHandeler {

    @Override
    public void handle() {

        System.out.println("欢迎使用美团打车");

    }

}

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

public static void main(String []args){

    SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();

    saveMoneyHandler.execute();

}

输出结果:

模版设计方法实践
打开了美团
欢迎使用美团打车
我点的外卖太好吃了

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

因为我们要在外卖平台有很多业务,所以可能需要定义很多的实现类:

//理发业务的实现类

public class HairdressingBusiness extends AbstractBusinessHandeler {

    @Override

    public void handle() {

        System.out.println("我点开了理发");

    }

}


//旅游业务的实现类

public class TravelBusiness extends AbstractBusinessHandeler{

    @Override

    public void handle() {

        System.out.println("我点开了旅游");

    }

}

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

但是,有了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/185928.html

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

相关文章

网络 随笔 2-linux的三种网络模式

0. 前面的科普对操作系统网络的理解还有帮助的 简单点&#xff0c;linux三种网络模式 linux中的三种网络模式 1. bridge 物理网卡使用虚拟网桥作为虚拟交换机的输入物理机以及虚拟网卡接入这个虚拟交换机虚拟网卡与物理网卡处于一个网段下(网关与DNS 一致) 2. NAT 虚拟的N…

删除的文件怎么恢复?误删文件恢复,就使用这些方法!

电脑里面保存着很多文件&#xff0c;为了让电脑更整洁&#xff0c;我们一般都会定期清理不必要的数据。在清理过程中&#xff0c;出现文件被误删&#xff0c;我们该怎么办&#xff1f;误删文件恢复&#xff0c;方法就看下面三个&#xff1a;注册表恢复、回收站恢复、软件恢复。…

一场晚会直播背后的安全攻防

多姿多彩的数字世界中&#xff0c;“直播”扮演了不可或缺的角色。刚刚结束的央视春晚&#xff0c;腾讯和中央广播电视总台一起打造了“竖屏春晚HDR及菁彩声”技术方案&#xff0c;并在“央视频”客户端上线。让广大用户“听”得更沉浸&#xff0c;“看”得更清晰。总台首次使用…

无刷电机驱动器

0.0参考&#xff1a; FOC?看这篇文章就够了 志辉君——【自制FOC驱动器】深入浅出讲解FOC算法与SVPWM技术 SPWM基本原理详解&#xff08;图文并茂公式推导C程序实现&#xff09; 1、开源的FOC方案 1、SmipleFOC是比较常见的无刷驱动方案&#xff0c;因为其便宜的制造成本…

高级Spring之Scope 详解

在当前版本的 Spring 和 Spring Boot 程序中&#xff0c;支持五种 Scope singleton&#xff0c;容器启动时创建&#xff08;未设置延迟&#xff09;&#xff0c;容器关闭时销毁 prototype&#xff0c;每次使用时创建&#xff0c;不会自动销毁&#xff0c;需要调用 DefaultList…

使用Plotly和Python进行交互式数据可视化

使用Plotly和Python进行交互式数据可视化 Python是数据探索和数据分析的好帮手&#xff0c;这都要归功于numpy、pandas、matplotlib等神奇库的支持。在我们的数据探索和数据分析阶段&#xff0c;理解我们正在处理的数据是非常重要的&#xff0c;为此&#xff0c;数据的可视化表…

Swin-Transformer算法解析

本文参考&#xff1a; SwinTransformer&#xff1a;使用shifted window的层级Transformer(ICCV2021)_tzc_fly的博客-CSDN博客 https://zhuanlan.zhihu.com/p/430047908 目录 1 为什么在视觉中使用Transformer 2 Swin-Transformer算法总体架构 3 Swin-Transformer Block详述…

C# 源码 等值线(等高线)云图绘制 ,图上含等高线数值

C# 源码 数据格式为XYZ数据&#xff0c;XY为坐标&#xff0c;Z为对应的值 X Y Z -0.671053 -0.850000 83.330742 -0.671053 -0.850000 85.469604 -0.671053 -0.760526 89.225899 -0.671053 -0.760526 86.994576 -0.671053 -0.760526 86.994576 -0.671053 -0.760526 89.225899 -…

【解读】《云事件响应框架》:云服务用户响应和管理事件首选指南

微信搜索”国际云安全联盟“&#xff0c;回复关键词“云事件”下载本报告 当今互联时代&#xff0c;全面的事件响应策略对于需要管理与降低风险的组织必不可少。然而&#xff0c;在基于云的基础设施和系统的事件响应策略方面&#xff0c;部分由于云的责任共担特性&#xff0c;…

sql的四种连接——左外连接、右外连接、内连接、全连接

一、内连接 满足条件的记录才会出现在结果集中。 二、 左外连接&#xff08;left outer join&#xff0c;outer可省略&#xff09; 左表全部出现在结果集中&#xff0c;若右表无对应记录&#xff0c;则相应字段为NULL 举例说明&#xff1a; 客户表&#xff1a; 订单表&#x…

2023年2月系统集成项目管理工程师认证【报名入口】

系统集成项目管理工程师是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目之一&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职…

Qt 根据参数 自动生成vs 工程

一&#xff0c;需求 给算法部门提供一套代码框架&#xff0c;让其写算法dll。为了使dll能融入主工程&#xff0c;其框架对格式有一定要求&#xff0c;为了增加算法部门的快发效率&#xff0c;因此开发一个小工具&#xff0c;用于自动生成这套框架。 运行后&#xff0c;只需要…

cdh+dolphinscheduler开启kerberos

搭建环境多台linux主机搭建集群CDH 6.3.2 (Parcel)版本dolphinscheduler 1.3.2版本本流程在CDH已搭建完成并可正常使用后&#xff0c;开启kerberos功能dolphinscheduler用于大数据任务管理与执行&#xff0c;是很不错的任务调度平台&#xff0c;是否提前部署均可开启kerberos目…

数据结构与算法:二叉树的学习

1.了解树形结构 1.概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。它具有以下的特点&#xff1a; …

《Unity Shader 入门精要》 第7章 基础纹理

第7章 基础纹理 纹理最初的目的就是使用一张图片来控制模型的外观。使用纹理映射技术&#xff08;texture mapping&#xff09;&#xff0c;我们可以把一张图黏在模型表面&#xff0c;逐纹素(texel)&#xff08;纹素的名字是为了和像素进行区分&#xff09;地控制模型的颜色。…

爱了爱了,这是什么神仙级Apache Dubbo实战资料,清晰!齐全!已跪!

都2026年了 还没有用过Dubbo&#xff1f; Dubbo是国内最出名的分布式服务框架&#xff0c;也是 Java 程序员必备的必会的框架之一。Dubbo 更是中高级面试过程中经常会问的技术&#xff0c;面试的时候是不是经常不能让面试官满意&#xff1f;无论你是否用过&#xff0c;你都必须…

Postman(2): postman发送带参数的GET请求

发送带参数的GET请求示例&#xff1a;微信公众号获取access_token接口&#xff0c;业务操作步骤1、打开微信公众平台&#xff0c;微信扫码登录&#xff1a;https://mp.weixin.qq.com/debug/cgi-bin/sandbox?tsandbox/login2、打开微信开放文档&#xff0c;找到获取access_toek…

运放电路中各种电阻的计算-运算放大器

运放电路中各种电阻的计算 在学习运算放大器电路的时候&#xff0c;经常需要计算电路的: 输入阻抗Ri&#xff0c; 输出阻抗Ro&#xff0c; 同相端对地等效电阻RP, 反相端对地等效电阻RN&#xff0c; 这些参数很重要&#xff0c;在学习运放相关电路的时候经常要用到&#…

mysql8+mybatis-plus 查询json格式数据

sql 测试json表CREATE TABLE testjson (id int NOT NULL AUTO_INCREMENT,json_obj json DEFAULT NULL,json_arr json DEFAULT NULL,json_str varchar(100) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;IN…

API 网关策略二三事

作者暴渊&#xff0c;API7.ai 技术工程师&#xff0c;Apache APISIX Committer。 近些年随着云原生和微服务架构的日趋发展&#xff0c;API 网关以流量入口的角色在技术架构中扮演着越来越重要的作用。API 网关主要负责接收所有请求的流量并进行处理转发至上游服务&#xff0c;…