JAVA8-lambda表达式8:在设计模式-模板方法中的应用

这篇具有很好参考价值的文章主要介绍了JAVA8-lambda表达式8:在设计模式-模板方法中的应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

传送门

JAVA8-lambda表达式1:什么是lambda表达式

JAVA8-lambda表达式2:常用的集合类api

JAVA8-lambda表达式3:并行流,提升效率的利器?

JAVA8-lambda表达式4:Optional用法

java8-lambda表达式5:toMap引发的线上故障

JAVA8-lambda表达式6:重构和定制收集器

JAVA8-lambda表达式7:重要的函数接口

如何看待写代码这件事

最近在公司写代码(包括看代码),突然有一点小小的感叹。好多人整天研究什么高并发,高可用,分布式,开口架构闭口新技术,就是不愿意花时间把自己的JAVA代码写的好一点。

把代码写好就是给自己印的最好的名片,也是对同事最大的负责!

可惜好多人不这样认为,或者说可能是现在环境就是这样吧:面试各种的造火箭,考算法导致从业者只能投其所好,刷题/刷各种高大上的所谓架构技术,而忽视一个最本质的前提!

那就是技术是为业务服务的,绝大部分的公司是用不上所谓的大厂架构的,强行匹配只会适得其反。

有空还是多琢磨琢磨怎么把那点JAVA代码写的更好吧。

这里的说法有点属于"夹带私货"了,太过片面了,切勿对号入座。

什么是模板方法

在很早以前(真的是很早了,看了下发布日期是2018年2月!),学习过模板方法,它属于常用的设计中的一种。当时里面介绍的例子取自《Head First设计模式》,所以文章算作是翻译过来的。例子比较简单,实现也是用的继承+多态的。而还有一种很常用的模板方法,就是一个类+静态方法,使用者直接通过静态方法来调用!

Template.execute(a,b);
  • Template为类名
  • execute为方法
  • a,b为参数

在上面的调用中,Template.execute是不会变化,顾名思义就是模板方法的意思。而a,b则是需要调用方传递的参数,必须是模板规定的类型。下面就以一个实际场景来看看如何抽象一个模板方法。

接口调用场景

对于JAVA程序员来说,spring肯定绕不开的结。当需要写一个后端接口的时候,通过springMVC可以很方便的来实现,比如在Oauth2系列7:授权码和访问令牌的颁发流程是怎样实现的?里面提到的准备工作-验证基本信息:

@RestController
@RequestMapping("/auth")
public class OauthController
{
    
    @GetMapping("/authorize")
    public void authorize(@RequestParam("response_type") String responseType, @RequestParam("client_id") String clientId,
                          @RequestParam("redirect_uri") String redirectUri, String scope)
    {
        
    }
}

还有验证客户端-生成访问令牌:

@PostMapping("/token")
    public TokenModel getToken(@RequestBody GetTokenRequest getTokenRequest) {
        // 获取令牌前置检验
        preGetTokenCheck(getTokenRequest);
 
        // 检验授权码
        checkCode(getTokenRequest.getCode());
 
        // 生成t访问令牌
        TokenModel tokenModel = generateToken();
        return tokenModel;
    }
private TokenModel generateToken() {
        // 获取code信息,比如从redis
       // CodeModel codeModel = getCode;
        TokenModel tokenModel = new TokenModel();
        tokenModel.setAccessToken(UUID.randomUUID().toString());
        tokenModel.setExpiresIn(3600);
        tokenModel.setRefreshToken(UUID.randomUUID().toString());
        // tokenModel.setScope(codeModel.getScope);
        return tokenModel;
    }
  • 在类上打上注解RestController或Controller,现在一般自动转换json就用RestController
  • 在方法上打上注解RequestMapping或GetMapping/PostMapping等,表示这个是一个接口方法
  • 在方法里面打上注解RequestParam或PathVariable,用来获取参数

大致按照这3个步骤来操作,剩下的主要就是业务代码编写了(实际项目里面也没有什么大的区别)。一般项目会分层,简单的就三层:Web/Service/Dao。

  • Web表示展示层:接口的入参获取,参数检验;日志打印;响应转换/返回(包括异常处理)
  • Service就是业务层:处理业务逻辑的,是方法的主体代码
  • Dao层称为存储层:一般表示db处理,也可以是其它持久操作

模板方法

根据上面简单三层的理解,定义一个模板方法出来:

@Slf4j
public class WebTemplate
{
    
    public static String execute(String req)
    {
        try
        {
            // 1:打印入参
            log.info("方法参数:{}", req);
            // 2:参数检验
            // TODO
            // 3:业务方法
        }
        catch (Exception e)
        {
            // 4:异常处理
            return "fail";
        }
        return "success";
    }
}

这里定义了模板方法的步骤:

  • 入参类型(如果是上面的例子只能的String肯定有局限性),最好支持泛型,比如都继承Request基类
  • 参数打印:将参数都打印出来,方便统计排查
  • 参数检验:对输入参数进行检验,如果不符合条件则抛出异常,让步骤4异常来统一处理
  • 业务方法执行:对于这种业务方法的执行,可以定义一个接口让调用方来实现
  • 异常执行:异常可以分为业务/全局异常,进行统一的处理,直接抛出或转换成对应异常码
  • 组装响应:响应可以自定义,比如如上的异常码/异常信息
  • 后置处理:可以在方法结束时,进行需要的后置处理,比如打印日志,方便后续监控

由此可见一个完整模板方法类似如下:

package com.tw.tsm.base.template;

import com.tw.tsm.base.request.BaseRequestDTO;
import com.tw.tsm.base.response.BaseResponseDTO;
import lombok.extern.slf4j.Slf4j;

/**
 * 模板方法类
 */
@Slf4j
public class WebTemplate
{
    
    /**
     * 模板方法
     * @param req 请求参数
     * @param res 响应参数
     * @param callback 回调方法
     * @param <T>
     * @param <R>
     */
    public static <T extends BaseRequestDTO, R extends BaseResponseDTO> void execute(T req, R res, ServiceCallback callback)
    {
        try
        {
            // 1:打印入参
            log.info("方法参数:{}", req);
            // 2:参数检验
            callback.check(req);
            // 3:业务方法
            callback.doService(req);
        }
        catch (Exception e)
        {
            // 4:异常处理
        }
        finally
        {
            log.info("处理结果:{}", res);
        }
    }
}


/**
 * 请求基类
 */
public class BaseRequestDTO
{
}


/**
 * 响应基类
 */
@Data
public class BaseResponseDTO<T>
{
    /** 错误码 */
    private String code;
    
    /** 错误信息 */
    private String msg;
    
    /** 返回内容 */
    private T data;
}

/**
 * 模板处理接口
 * 
 * @param <T>
 */
public interface ServiceCallback<T>
{
    void check(T req);
    
    void doService(T req);
}

至此,模板方法已经初具雏形,对于调用方来说,即可如下:

WebTemplate.execute(req, new BaseResponseDTO(), new ServiceCallback() {
            @Override
            public void check(Object req) {
                // 参数检验
            }

            @Override
            public void doService(Object req) {
                // 业务方法
            }
        });

异常处理 

上面的模板方法,统一捕获了异常Exception,这样所有的异常都会被处理。不过在很多时候,可能需要对异常进行分类处理,比如将异常分为业务/系统异常:

  • 对于业务异常希望明确提示调用方,告诉它失败的原因:比如参数不合法,重复提交,资源不存在等
  • 对于系统异常,比如db连接超时,第三方服务不可用,Npe异常等又不希望直接返回给调用者:因为这种错误一般不是逻辑错误,调用者感知了也解决不了;再者可能会暴露代码堆栈信息,也不太安全。所以这种一般处理是返回默认的提示,比如"系统繁忙,请稍候重试"

基于此,定义一个异常类:CheckedException让它继承自RuntimeException表示业务异常,跟Exception做区分:

/**
 * 检验异常
 */
@Data
@RequiredArgsConstructor
public class CheckedException extends RuntimeException
{
    
    /** 错误码 */
    private String code;
    
    /** 错误信息 */
    private String msg;
}

在模板方法里面,加入CheckException处理:

/**
 * 模板方法类
 */
@Slf4j
public class WebTemplate
{
    
    /**
     * 模板方法
     * @param req 请求参数
     * @param res 响应参数
     * @param callback 回调方法
     * @param <T>
     * @param <R>
     */
    public static <T extends BaseRequestDTO, R extends BaseResponseDTO> void execute(T req, R res, ServiceCallback callback)
    {
        try
        {
            // 1:打印入参
            log.info("方法参数:{}", req);
            // 2:参数检验
            callback.check(req);
            // 3:业务方法
            callback.doService(req);
        }
        catch (Exception e)
        {
            // 4:异常处理
            handleException(e, res);
        }
        finally
        {
            log.info("处理结果:{}", res);
        }
    }
    
    /**
     * 异常处理
     * @param e
     * @param response
     */
    private static void handleException(Exception e, BaseResponseDTO response)
    {
        if (e instanceof CheckedException)
        {
            CheckedException ex = (CheckedException)e;
            ex.setCode(ex.getCode());
            ex.setMsg(ex.getMsg());
            return;
        }
        // 设置默认异常
        // TODO
    }
}

异常码

在模板类中单独定义了一个方法handleException来处理异常:如果是业务异常,则将异常中的错误码/错误信息填充到响应里面,如果是系统异常则填充默认异常。因为这里定义的只有一个异常类CheckException来表示所有的业务异常,所以对于业务异常需要单独定义一个类来表示不同的场景:

/**
 * 通用错误码
 */
@AllArgsConstructor
public enum ComErrorCode
{
    
    /** 缺少参数 */
    PARAM_MISS("E01000", "缺少参数"),
    /** 系统繁忙,请稍候重试 */
    SYSTEM_ERROR("E01999", "系统繁忙,请稍候重试");
    
    @Getter
    private String code;
    
    @Getter
    private String msg;
}

模板方法补充如下:

/**
 * 模板方法类
 */
@Slf4j
public class WebTemplate
{
    
    /**
     * 模板方法
     * @param req 请求参数
     * @param res 响应参数
     * @param callback 回调方法
     * @param <T>
     * @param <R>
     */
    public static <T extends BaseRequestDTO, R extends BaseResponseDTO> void execute(T req, R res, ServiceCallback callback)
    {
        try
        {
            // 1:打印入参
            log.info("方法参数:{}", req);
            // 2:参数检验
            callback.check(req);
            // 3:业务方法
            callback.doService(req);
        }
        catch (Exception e)
        {
            // 4:异常处理
            handleException(e, res);
        }
        finally
        {
            log.info("处理结果:{}", res);
        }
    }
    
    /**
     * 异常处理
     * @param e
     * @param response
     */
    private static void handleException(Exception e, BaseResponseDTO response)
    {
        if (e instanceof CheckedException)
        {
            CheckedException ex = (CheckedException)e;
            ex.setCode(ex.getCode());
            ex.setMsg(ex.getMsg());
            return;
        }
        // 设置默认异常
        response.setCode(ComErrorCode.SYSTEM_ERROR.getCode());
        response.setMsg(ComErrorCode.SYSTEM_ERROR.getMsg());
    }
}

lambda在模板方法中的应用

既然是要用lambda表达式在模板方法中应用,所以就不能像刚才那样对于回调函数直接用匿名类,这里就改造一下:文章来源地址https://www.toymoban.com/news/detail-619903.html

/**
 * 模板回调函数
 * 
 * @param <T>
 */
@FunctionalInterface
public interface ServiceCallback<T>
{
    default void check(T req)
    {
    }
    
    void doService(T req);
}


 WebTemplate.execute(req, new BaseResponseDTO(), request -> {
        });
  • 首先改造一下回调函数,增加@FunctionalInterface注解,表示这是一个函数式接口
  • 将check()方法声明为default,这里java8的默认方法
  • 最后用lambda实现业务处理逻辑

到了这里,关于JAVA8-lambda表达式8:在设计模式-模板方法中的应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Java8新特性1——函数式接口&lambda表达式

    注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过 目录: Java8新特性1——函数式接口lambda表达式 Java8新特性2——方法引用 Java8新特性3——Stream 如果在一个接口中, 有且只有一个抽象方法 ,则该接口被称为函数式接口。如: 注: 可以在接口前使用 @FunctionalInt

    2024年02月10日
    浏览(23)
  • 进阶JAVA篇- Lambda 表达式与 Lambda 表达式的省略规则

    进阶JAVA篇- Lambda 表达式与 Lambda 表达式的省略规则

    目录         1.0 什么是 Lambda 表达式?         1.1 既然跟匿名内部类相关,先来回顾匿名内部类。          1.2 Lambda 表达式与匿名内部类之间的关系。         1.3 函数式接口         1.4 在具体代码中来操作一下         2.0 Lambda 表达式省略规则          Lambda 表达

    2024年02月08日
    浏览(12)
  • Java- Lambda表达式

    目录 一、Lambda简介 二、Lambda使用前提 三、Lambda语法 1.操作符 a.\\\"-\\\"  b.\\\"::\\\" 2.格式  a.无参数 b.有参数 四、Lambda演化过程 五、Lambda实现排序 Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)

    2024年02月03日
    浏览(16)
  • Java Lambda表达式

    1.1 函数式编程思想概括 在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作” 面向对象思想强调“必须通过对象的形式来做事情” 函数式思想则尽量忽略面

    2024年02月07日
    浏览(20)
  • Java Lambda 表达式

    Java Lambda 表达式

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Java Lambda 表达式是 Java 8 引入的一种函数式编程特性,它是一种轻量级的匿名函数,允许我们将函数作为方法的参数进行传递。Lambda 表达式可以理解为是一种简洁的方式来表示可传递的代码块,它可以替代传统的匿名内

    2024年02月08日
    浏览(21)
  • Lambda表达式(JAVA)

    Lambda表达式(JAVA)

    注:如果没有学过 匿名内部类 和 接口 不推荐往下看。 (parameters) - expression 或 (parameters) -{ statements; } parameters:表示参数列表; -:可理解为“被用于”的意思; expression:表示一条语句; statements:表示多条语句。 Lambda可以理解为:Lambda就是匿名内部类的简化。 lambda表达式

    2024年02月08日
    浏览(12)
  • 【JAVA】包装类、正则表达式、Arrays类、Lambda表达式

    包装类是8种基本数据类型对应的引用类型 作用:后期的集合和泛型不支持基本类型,只能使用包装类 基本数据类型和其对应的引用数据类型的变量可以互相赋值 基本数据类型 引用数据类型 byte Byte short Short int Integer long Long char Character float Float double Double boolean Boolean 包装类

    2024年02月13日
    浏览(18)
  • java lambda表达式详解

    java lambda表达式详解

    我们知道,在Java中,接口是不能实例化的,但是接口对象可以指向它的实现类对象。如果接口连实现对象都没有呢?那还可以使用匿名类的方式,如下: 复制 但是,使用匿名内部的方式,代码量其实并不是非常简洁,而为了使代码更加的简洁,Java引进了 Lambda 表达式的写法,

    2024年02月03日
    浏览(19)
  • Java中的lambda表达式

    Java中的lambda表达式

    目录 一、背景 二、lambada表达式的基本使用 三、变量捕获 四、lambda在集合中的使用(map和set) 五、优缺点 一、背景 (1)、lambda表达式的定义: (2)、lambda表达式的语法: (3)、函数式接口 二、lambada表达式的基本使用 (1)没有使用lambda表达式,简单调用函数式接口展

    2024年02月08日
    浏览(12)
  • Java学习——lambda表达式

    Java学习——lambda表达式

    什么是Lambda表达式? 可以将Lambda表达式理解为一个匿名函数; Lambda表达式允许将一个函数作为另外一个函数的参数; 我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程, 将一个函数作为参数进行传递 。 为什么要引入Lambda表

    2024年02月04日
    浏览(18)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包