资讯专栏INFORMATION COLUMN

利用函数式优雅的重构API

bergwhite / 3291人阅读

摘要:新建字典必要的验证参数不能为空调用封装数据到统一到结构体异常处理包括中抛出到自定义异常和未知异常统一包装到返回对象中这样子的代码在开发接口的时候比比皆是。

基础知识

在开始之前 假设你从未了解过函数式编程,本文不会教你函数式里面的各种概念。而是用实际案例让给了解到函数式编程的魅力,并且能够在合适的场景中应用。

## 重构之前

基本代码如下 很普通的 springboot http接口。

@RequiresRoles(roles = {RoleEnum.ADMIN_CENTER})
   @ApiOperation(value = "新建字典")
   @PostMapping("save")
   public Response saveDict(@RequestBody SaveDictVO vo) {


       Response response;
       try {
           // 1 必要的验证参数
           if (StringUtils.isBlank(vo.getTypeValue())) {
               response = new Response(ResponseStatus.BAD_REQUEST.getCode(), "typeValue不能为空");


           } else {
               // 2.调用service
               boolean b = dictTypeService.saveDictType(vo);

               response = new Response(ResponseStatus.OK.getCode(), ResponseStatus.OK.getMsg());
               // 3 封装数据到统一到结构体
               response.setData(b);

               return response;
           }
       } catch (Exception e) {
           // 4.异常处理  包括 service中抛出到自定义异常和未知异常统一包装到 返回对象中
           response = GlobalControllerExceptionHandler.resolveExceptionCustom(e);
       }

       return response;


   }

这样子的代码 在开发http接口的时候比比皆是。本身写的不坏,没有拗口的地方,逻辑很严谨,读起来很流畅。
美中不足的是,不够通用化。 大量这样的代码容易让编码的人变成一个不会思考的机器...

简单重构 分析

对比之前写的每个http接口我们很容易发现它们有相似的特点

必要的验证参数

调用service

封装数据到统一到结构体

异常处理 包括 service中抛出到自定义异常和未知异常统一包装到 返回对象中

只有第二步是不同的,其他步骤几乎是重复的逻辑。

先做简单的

我们可以抽取一个方法把 1,3,4做统一处理

public Response action(Object req) {

        try {
            String vstr = ValidatorUtils.validate(req);
            if (StringUtils.isNotBlank(vstr)) {
                return new Response(ResponseStatus.BAD_REQUEST.getCode(), vstr);
            }
            Object result = null; // 调用servcie 执行结果
            Response response = new Response(ResponseStatus.OK.getCode(), ResponseStatus.OK.getMsg());
            response.setData(result);

            return response;
        } catch (Exception e) {
            log.error("", e);
            Response response = GlobalControllerExceptionHandler.resolveExceptionCustom(e);
            return response;
        }
    }
用接口抽象service逻辑
public interface ActionCallBack {

    Object action(Object req);


}

修改上面的action方法

public Response action(Object req, ActionCallBack action) {

        try {
            String vstr = ValidatorUtils.validate(req);
            if (StringUtils.isNotBlank(vstr)) {
                return new Response(ResponseStatus.BAD_REQUEST.getCode(), vstr);
            }
            Object result = action.action(req);
            Response response = new Response(ResponseStatus.OK.getCode(), ResponseStatus.OK.getMsg());
            response.setData(result);

            return response;
        } catch (Exception e) {
            log.error("", e);
            Response response = GlobalControllerExceptionHandler.resolveExceptionCustom(e);
            return response;
        }
    }
如何使用
@RequiresRoles(roles = {RoleEnum.ADMIN_CENTER})
    @ApiOperation(value = "新建字典")
    @PostMapping("save")
    public Response saveDict(@RequestBody SaveDictVO vo) {


        return action(vo, new ActionCallBack() {
            @Override
            public Object action(Object req) {
                //业务逻辑
                return dictTypeService.saveDictType(vo);
            }
        });


    }
引入函数式

当我们重构完上面的代码,并在 saveDict方法中引入使用时,一切看起来都很美好。但是IDE却给出一些善良的提示.

nonymous new ActionCallBack() can be replaced with lambda less... (⌘F1) 
Inspection info: Reports all anonymous classes which can be replaced with lambda expressions
Lambda syntax is not supported under Java 1.7 or earlier JVMs.

根据IDEA 提示将代码修改成函数式风格

@RequiresRoles(roles = {RoleEnum.ADMIN_CENTER})
    @ApiOperation(value = "新建字典")
    @PostMapping("save")
    public Response saveDict(@RequestBody SaveDictVO vo) {


        return action(vo, req -> {
            //业务逻辑
            return dictTypeService.saveDictType(vo);
        });


    }
引入原生函数式接口
public  Response action(R req, Function action) {

        try {
            String vstr = ValidatorUtils.validate(req);
            if (StringUtils.isNotBlank(vstr)) {
                return new Response(ResponseStatus.BAD_REQUEST.getCode(), vstr);
            }
            Object result = action.apply(req);
            Response response = new Response(ResponseStatus.OK.getCode(), ResponseStatus.OK.getMsg());
            response.setData(result);

            return response;
        } catch (Exception e) {
            log.error("", e);
            Response response = GlobalControllerExceptionHandler.resolveExceptionCustom(e);
            return response;
        }
    }
最终成果
@Api(value = "指标API", description = "指标API")
@RestController
@RequestMapping(ConfigConstants.CURRENT_API_VERSION + "/indicators")
public class IndicatorsController extends BaseController {

    @Autowired
    private IndicatorsService indicatorsService;

    @RequiresRoles(roles = {RoleEnum.ADMIN_CENTER})
    @ApiOperation(value = "新增指标")
    @PostMapping("/save")
    public Response save(@ApiIgnore @ModelAttribute DwSalaryEmpInfo empInfo, @RequestBody AddIndicatorsVO vo) {
        return action(vo, v -> {
            Indicators model = BeanCopyUtils.copy(vo, new Indicators());
            Indicators record = indicatorsService.saveIndicators(empInfo, model);
            return BeanCopyUtils.copy(record, new IndicatorsVO());
        });
    }

    @RequiresRoles(roles = {RoleEnum.ADMIN_CENTER})
    @ApiOperation(value = "删除指标")
    @PostMapping("/remove")
    public Response remove(@ApiIgnore @ModelAttribute DwSalaryEmpInfo empInfo, @RequestParam String id) {
        return action(() -> indicatorsService.removeIndicators(id));
    }
}
小结

上面代码来自我自己的实际项目一个缩影,实际上做一个上层的封装要处理很多问题,比如某些接口不需要参数验证,或者不需要返回值,就需要对action函数进行重载。让业务更方便的使用。

通过使用函数式变成对过程/对象混合代码进行重构,使得代码更凝练而有表达力了。虽然函数式编程尚未广泛推广于大型工程中,只有一部分程序猿开始尝试使用,在理解上也需要一定的思维转换,不过适度使用确实能增强代码的抽象表达力。仅仅是“高阶函数+泛型+惰性求值”的基本使用,就能产生强大而凝练的表达效果。 函数式编程确有一套自己独特的编程设计理念。

现代软件开发已经不仅仅是单纯地编写代码实现逻辑,而是含有很强的设计过程。需要仔细提炼概念、对象、操作,仔细设计对象之间的交互,有效地组合一系列关联对象成为高内聚低耦合的模块,有效地隔离对象关联最小化依赖关系,如此才能构建出容易理解和扩展、更容易演进的长久发展的软件。编程即是设计,从具象到抽象再到具象的过程。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74753.html

相关文章

  • Python语言中计数方法演变

    摘要:译文链接编程派有时候,利用语言简洁优雅地解决问题的方法,会随着时间变化。随着不断进化,统计列表元素数量的方法也在改变。最后将字典中相应键的值设置为新的计数。我们发现这种方法比之前的代码更加简洁优雅,所以提交了此次修改。 showImg(https://segmentfault.com/img/remote/1460000010723715); 文中如对专业术语的翻译有误,请Pytho...

    sunsmell 评论0 收藏0
  • Hooks + Context:状态管理新选择

    摘要:用户点击改变全局状态崔然渲染整颗组件树有没有解决方案呢当然有创建一个只接收的新组件,并将组件中的逻辑都移到组件中。最终的示例使用全局状态和生成全局状态和崔然完整示例见结论在和出现之前,缺乏自带的全局状态管理能力。 React 16.3 版本,正式推了出官方推荐的 context API —— 一种跨层级的数据传递方法。React 16.8 版本,推出了全新的 hooks 功能,将原本只...

    tommego 评论0 收藏0
  • 响应设计个人一些总结

    摘要:所以一个网,甚至是响应式设计,在两个平台上都会损害您整体的。三响应式与如果把网站作为一个单独的网站,如果网站的内容与桌面版的内容相对缺少,导致用户回到桌面端的网站,会记录这种选择,使搜索排名降低,国内百度就不知道会怎样。 一、为什么需要响应式设计(responsible web design) 1. 响应式发展背景 1、屏幕尺寸的快速变化,iphone为320x480,分辨率在未来可以...

    LeoHsiun 评论0 收藏0
  • 重构 - 改善代码各方面问题

    摘要:暴露接口如果是函数,就扩展,否则就是验证数据使用金额校验规则这样运行能正常,也有扩展性性,但是对于代码洁癖的来说,这样写法不优雅。 重构不是对以前代码的全盘否定,而是利用更好的方式,写出更好,更有维护性代码。不断的追求与学习,才有更多的进步。 1.前言 做前端开发有一段时间了,在这段时间里面,对于自己的要求,不仅仅是项目能完成,功能正常使用这一层面上。还尽力的研究怎么写出优雅的代码,性...

    AlphaWallet 评论0 收藏0

发表评论

0条评论

bergwhite

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<