基于构建者模式的返回值统一

人在江湖飘,不懂设计模式装X?

一、什么是模式?

在了解设计模式之前,我们还是应该先了解一下设计模式的发展背景。

说起设计模式的前世,我们先要了解一下模式是如何诞生与发展的。与很多软件工程技术一样,模式起源于建筑工程领域。由杰出理论建筑家、公认的模式之父克里斯托弗·亚力山大(Christopher Alexander)在《建筑模式语言》一书中指出-“每个模式都描述了一个在我们的环境中不断出现的的问题,然后描述了该问题的解决方案的核心,通过这种方式,你可以无数次使用那些已有的解决方案,无需再重复相同的工作”。

《建筑模式语言》 一出版就受到建筑界的广泛重视和高度赞誉,并对建筑业产生了深远的影响。书中别出心裁且有根有据地描述了城镇、邻里、住宅、花园和房间等共253个模式,提供了一幅幅设计、规划、施工等方面的崭新蓝图,构思新奇,妙想迭出,不同流俗。

基于构建者模式的返回值统一

最早将模式这个概念引入到软件工程的是由GoF(Gang of Four)四人组(Erich Gamma,Richard Helm,Ralph JohnsonJohn Vlissides)于1994年发表了  《Design Patterns Elements of Reusable Object-Oriented Software》又称 《设计模式-可复用面向对象软件的基础》 该书详细讲解了可用于软件工程领域的 23种 常用设计模式。

软件模式的基础结构主要由四部分构成:

  • 问题描述:需要解决的问题
  • 前提条件:解决问题的前提条件
  • 解觉方案:问题的解决方案
  • 效果:达成的效果,以及基于效果分析出优缺点
基于构建者模式的返回值统一

二、构建者模式介绍

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

构建者模式(Builder Pattern)也称为建造者模式,使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

意图

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

主要解决

主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用

一些基本部件不会变,而其组合经常变化的时候。

如何解决

将变与不变分离开。

关键代码

  • 建造者:创建和提供实例;
  • 导演:管理建造出来的实例的依赖关系。

优点

分离构建过程和表示,使得构建过程更加灵活,可以构建不同的表示。可以更好地控制构建过程,隐藏具体构建细节。代码复用性高,可以在不同的构建过程中重复使用相同的建造者。

缺点

如果产品的属性较少,建造者模式可能会导致代码冗余。建造者模式增加了系统的类和对象数量。

使用场景

1、需要生成的对象具有复杂的内部结构。

2、需要生成的对象内部属性本身相互依赖。

建造者模式在创建复杂对象时非常有用,特别是当对象的构建过程涉及多个步骤或参数时。它可以提供更好的灵活性和可维护性,同时使得代码更加清晰可读。

⚠️注意事项

与工厂模式的区别是建造者模式更加关注与零件装配的顺序。


可能各位看上面的定义可能觉得太抽象了,举个通俗的例子:

就拿肯德基套餐来说吧,一个经典的套餐就是一个汉堡+可乐+薯条,这三种食品是可选的,我们也可以汉堡+可乐推出新的套餐,而我们要解决的问题就是怎么样去搭配套餐。

假设,套餐由主食+辅食+饮品构成,且三种皆是可选。

/**
* 套餐
*/

public class ComboMeal {
    private String staple;//主食
    private String supplement;//辅食
    private String drink;//饮品
}

若使用new关键字去构造我们的实例对象,就显得有点不友好了,这时候针对这种场景,可以考虑使用构建者模式。

public class ComboMeal {
    ...
    public ComboMeal(String staple) {
        this(staple, nullnull);
    }
    public ComboMeal(String supplement) {
        this(null, supplement, null);
    }
    public ComboMeal(String drink) {
        this(nullnull, drink);
    }
    public ComboMeal(String supplement, String drink) {
        this(null, supplement, drink);
    }
    public ComboMeal(String staple, String supplement, String drink) {
        this.staple = staple;
        this.supplement = supplement;
        this.drink = drink;
    }
    ...
}

可以看到上述这串代码,有两个弊端:

  • 主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易混淆。

  • 构建过程中对象的状态容易发生变化,造成错误。

如何实现?

  1. ComboMeal构建一个静态的内部构造器类
  2. ComboMeal创建一个builder()方法获取构造器
  3. ComboMealBuilderComboMeal的属性名作为方法名注入对应的值并返回当前构造器对象
  4. ComboMealBuilder中创建一个build()方法,在其中构建ComboMeal的实例并返回
/**
* 案例代码
*/

public class ComboMeal {
    private String staple;//主食
    private String supplement;//辅食
    private String drink;//饮品

    public ComboMeal(String staple, String supplement, String drink) {
        this.staple = staple;
        this.supplement = supplement;
        this.drink = drink;
    }

    public String getStaple() {
        return staple;
    }

    public void setStaple(String staple) {
        this.staple = staple;
    }

    public String getSupplement() {
        return supplement;
    }

    public void setSupplement(String supplement) {
        this.supplement = supplement;
    }

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }

    public static ComboMeal.ComboMealBuilder builder() {
        return new ComboMeal.ComboMealBuilder();
    }

    public static class ComboMealBuilder {
        private String staple;//主食
        private String supplement;//辅食
        private String drink;//饮品

        ComboMealBuilder() {
        }

        public ComboMealBuilder staple(String staple) {
            this.staple = staple;
            return this;
        }

        public ComboMealBuilder supplement(String supplement) {
            this.staple = supplement;
            return this;
        }

        public ComboMealBuilder drink(String drink) {
            this.staple = drink;
            return this;
        }

        public ComboMeal build() {
            return new ComboMeal(this.staple, this.supplement, this.drink);
        }
    }
}

经过上述代码分析,我们就已经大致了解了建造者模式的基本思路。接下来,我们可以针对一个常见的使用场景作为一个实战案例。

三、基于构建者模式的返回值统一

Web开发过程中,我们常因为后端返回值凌乱而感到痛苦,我就曾有幸见识到一位伟大的后端给前端返回的数据格式:

基于构建者模式的返回值统一

为了解决这一痛点,我们可以就可以使用构建者模式去统一返回值。

枚举类

@Getter
@AllArgsConstructor
public enum ResultCode {
    SUCCESS(HttpServletResponse.SC_OK, "操作成功"),
    FAILURE(HttpStatus.HTTP_INTERNAL_ERROR,"操作失败");
    private final Integer code;
    private final String message;
}

返回实例

@Data
@Builder
@Schema(name = "统一返回值")
public class ResponseResult<T{
    @Schema(name = "code",description = "状态码")
    private Integer code;

    @Schema(name = "message",description = "响应消息")
    private String message;

    @Schema(name = "data",description = "响应数据")
    private T data;

    @Builder.Default
    @Schema(name = "timestamp",description = "响应时间")
    private long timestamp = System.currentTimeMillis();

    @SuppressWarnings("unused")
    public static <T> ResponseResult<T> error() {
        return failure();
    }

    public static <T> ResponseResult<T> success(T data){
        return ResponseResult.<T>builder().data(data)
                .code(ResultCode.SUCCESS.getCode())
                .message(ResultCode.SUCCESS.getMessage())
                .build();
    }

    @SuppressWarnings("unused")
    public static <T> ResponseResult<T> success(){
        return ResponseResult.<T>builder()
                .code(ResultCode.SUCCESS.getCode())
                .message(ResultCode.SUCCESS.getMessage())
                .build();
    }

    public static <T> ResponseResult<T> failure(){
        return ResponseResult.<T>builder()
                .code(ResultCode.FAILURE.getCode())
                .message(ResultCode.FAILURE.getMessage())
                .build();
    }

    @SuppressWarnings("unused")
    public static <T> ResponseResult<T> failure(Integer code,String message){
        return ResponseResult.<T>builder()
                .code(code)
                .message(message)
                .build();
    }

}

其中@Builder注解就是声明使用构建者模式,同时@Builder.Default注解标识字段timestamp使用默认值,字段data是泛型实例,接收任意实例对象。


原文始发于微信公众号(青衫大叔灬):基于构建者模式的返回值统一

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/179757.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!