★
人在江湖飘,不懂设计模式咋 装X?
”
一、什么是模式?
在了解设计模式之前,我们还是应该先了解一下设计模式的发展背景。
说起设计模式的前世,我们先要了解一下模式是如何诞生与发展的。与很多软件工程技术一样,模式起源于建筑工程领域。由杰出理论建筑家、公认的模式之父克里斯托弗·亚力山大(Christopher Alexander)
在《建筑模式语言》一书中指出-“每个模式都描述了一个在我们的环境中不断出现的的问题,然后描述了该问题的解决方案的核心,通过这种方式,你可以无数次使用那些已有的解决方案,无需再重复相同的工作”。
《建筑模式语言》 一出版就受到建筑界的广泛重视和高度赞誉,并对建筑业产生了深远的影响。书中别出心裁且有根有据地描述了城镇、邻里、住宅、花园和房间等共253个模式,提供了一幅幅设计、规划、施工等方面的崭新蓝图,构思新奇,妙想迭出,不同流俗。

最早将模式这个概念引入到软件工程的是由GoF(Gang of Four)
四人组(Erich Gamma
,Richard Helm
,Ralph Johnson
、John 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, null, null);
}
public ComboMeal(String supplement) {
this(null, supplement, null);
}
public ComboMeal(String drink) {
this(null, null, 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;
}
...
}
可以看到上述这串代码,有两个弊端:
-
主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易混淆。
-
构建过程中对象的状态容易发生变化,造成错误。
如何实现?
-
为 ComboMeal
构建一个静态的内部构造器类 -
为 ComboMeal
创建一个builder()
方法获取构造器 -
在 ComboMealBuilder
以ComboMeal
的属性名作为方法名注入对应的值并返回当前构造器对象 -
在 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