是不是厌倦了大量if...else的代码,可以使用建造者模式来简化校验逻辑。
1. 原理
1.1 建造者模式
回顾一下,建造者模式几个要素:
- 私有的构造方法;
- public的静态方法,用于创建Builder对象;
- 静态内部类Builder,内部的方法返回this对象;
1.2 lamda表达式
因为大量的校验逻辑需要传入到方法中:
- 需要借助与lamda的Function方法。
- 为了简化Function参数,可以使用Predicate+String来构造Function。
1.3 友好异常和精确异常
需要借助工具类:[JAVA基础篇23]—JAVA异常工具类ExceptionUtils的使用
友好异常:实际用户看到的描述;
精确异常:开发可以快速定位问题的描述(包含异常的代码位置)。
2. 代码实现
2.1 可以完成的细节
- 友好异常与精确异常;
- 快速失败与完全失败;
- 异常终止与捕获异常;
2.2 定义Function的响应对象
需要定义一个响应对象,作为Function的返回对象。需要包含如下的功能:
@Getter
public class ValidationError {
/**
* 错误信息,友好错误信息
*/
private String friendlyErrorMsg;
/**
* 错误信息,精确的错误信息
*/
private String accurateErrorMsg;
/**
* 实际异常的参数
*/
private Object[] params;
/**
* 实际异常的属性名
*/
private String field;
/**
* 错误码
*/
private int errorCode;
public ValidationError(String friendlyErrorMsg, Object[] params) {
this.friendlyErrorMsg = friendlyErrorMsg;
this.params = params;
}
public ValidationError(String friendlyErrorMsg, String accurateErrorMsg, Object[] params) {
this.friendlyErrorMsg = friendlyErrorMsg;
this.accurateErrorMsg = accurateErrorMsg;
this.params = params;
}
public static ValidationError of(String friendlyErrorMsg, Object... params) {
return new ValidationError(friendlyErrorMsg, params);
}
public static ValidationError of(String friendlyErrorMsg, String accurateErrorMsg, Object... params) {
return new ValidationError(friendlyErrorMsg, accurateErrorMsg, params);
}
}
2.3 定义异常父类
用来在保存友好异常和精确异常:
@Getter
public class BaseException extends RuntimeException {
/**
* 错误信息,精确的错误信息
*/
private String accurateErrorMsg;
public BaseException(String accurateErrorMsg) {
this.accurateErrorMsg = accurateErrorMsg;
}
public BaseException(String message, String accurateErrorMsg) {
super(message);
this.accurateErrorMsg = accurateErrorMsg;
}
public BaseException(String message, Throwable cause, String accurateErrorMsg) {
super(message, cause);
this.accurateErrorMsg = accurateErrorMsg;
}
public BaseException(Throwable cause, String accurateErrorMsg) {
super(cause);
this.accurateErrorMsg = accurateErrorMsg;
}
public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace,
String accurateErrorMsg) {
super(message, cause, enableSuppression, writableStackTrace);
this.accurateErrorMsg = accurateErrorMsg;
}
}
2.4 建造者实现代码
建造者要素
/**
* 建造者模式,来完成(对一个对象)链式校验逻辑。
* 1. 私有的构造方法;
* 2. public的静态方法,创建Builder对象
* 3. 静态内部类 Builder完成主要逻辑;
* 4. Builder里面提供的方法,返回this;
* --------
* 1. 为了规范数据的输出,参数可以要求传入Predicate和String 在代码内部构造Function对象
*
*/
public class FunctionalValidatorExample {
private FunctionalValidatorExample() {
}
public static FunctionalValidatorExample.Builder checkFrom(T element) {
return new FunctionalValidatorExample.Builder<>(element);
}
public static class Builder {
/**
* 待校验的对象
*/
private T element;
/**
* 是否快速失败
*/
private boolean failFast = true;
private boolean catchException = false;
/**
* 校验逻辑对象
*/
private List> validators = new LinkedList<>();
Builder(T element) {
this.element = element;
}
/**
* 贪婪匹配后失败
*/
public FunctionalValidatorExample.Builder failOver() {
failFast = false;
return this;
}
/**
* 是否捕获异常
*/
public FunctionalValidatorExample.Builder catchException() {
catchException = true;
return this;
}
/**
* 校验逻辑
*
* @param validator 传入的方法逻辑,返回值规定了要返回ValidationError对象
* @return this对象
*/
public FunctionalValidatorExample.Builder on(Function super T, ValidationError> validator) {
validators.add(validator);
return this;
}
/**
* 校验逻辑,规定响应对象
*
* @param validatorPredicate 传入的校验逻辑
* @param errorMsg 当出现异常时,希望的返回值
* @return this对象
*/
public FunctionalValidatorExample.Builder on(Predicate super T> validatorPredicate, String errorMsg) {
Objects.requireNonNull(errorMsg, "errorMsg is null");
on(t -> validatorPredicate.test(t) ? null : ValidationError.of(errorMsg));
return this;
}
/**
* @param conditionPredicate 条件逻辑,条件为true时,才会执行validatorPredicate校验
* @param validatorPredicate 校验逻辑,当返回true则输出null,当返回false则输出errorMsg
* @param errorMsg 校验逻辑失败时返回值。
*/
public FunctionalValidatorExample.Builder onIf(Predicate super T> conditionPredicate,
Predicate super T> validatorPredicate, String errorMsg) {
onIf(conditionPredicate, t -> validatorPredicate.test(t) ? null : ValidationError.of(errorMsg));
return this;
}
/**
* @param conditionPredicate 条件逻辑,条件为true时,才会执行function方法
* @param validator 逻辑方法
*/
public FunctionalValidatorExample.Builder onIf(Predicate super T> conditionPredicate,
Function super T, ValidationError> validator) {
validators.add(t -> conditionPredicate.test(t) ? validator.apply(t) : null);
return this;
}
// 支持多对象校验
public Builder onNotEmpty(IGetter super T>... getters) {
Arrays.stream(getters).forEach(getter -> {
on(t -> isNotEmpty(getter.get(t)) ? null : ValidationError.of(String.format("参数异常,%s为空", convertToFieldName(getter))));
});
return this;
}
/**
* 整合所有的异常描述
*/
public ValidationResult check() {
//返回对象
ValidationResult result = ValidationResult.build();
//校验逻辑
for (Function super T, ValidationError> validator : validators) {
ValidationError validationError;
try {
validationError = validator.apply(element);
} catch (BaseException e) {
if (!catchException) {
throw e;
}
validationError = ValidationError.of(e.getMessage(), e.getAccurateErrorMsg());
} catch (Exception e) {
if (!catchException) {
throw e;
}
validationError = ValidationError.of("网络异常,请稍后再试", ExceptionUtil.getLogErrorMessage(e));
}
//加入到异常类中
result.addErrors(validationError);
//存在失败的
if (failFast && !result.isSuccess()) {
break;
}
}
return result;
}
}
}
存储异常的类:
/**
* 存储校验结果的类
*/
@Getter
public class ValidationResult {
private List sourceErrors = new LinkedList<>();
private boolean success = true;
private ValidationResult() {
}
public static ValidationResult build() {
return new ValidationResult();
}
/**
* 增加错误信息
*/
public ValidationResult addErrors(ValidationError... errors) {
if (errors != null && errors.length > 0) {
sourceErrors.addAll(Arrays.asList(errors));
success = false;
}
return this;
}
/**
* 增加错误信息
*/
public ValidationResult addErrors(Collection errorList) {
if (errorList != null && !errorList.isEmpty()) {
sourceErrors.addAll(errorList);
success = false;
}
return this;
}
}
如何获取到传入对象的name
@FunctionalInterface
public interface IGetter extends Serializable {
Object get(T source);
}
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SerializedLambdaUtil {
private static Map CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>(32);
/***
* 转换方法引用为属性名
*/
public static String convertToFieldName(IGetter fn) {
SerializedLambda lambda = getSerializedLambda(fn);
String methodName = lambda.getImplMethodName();
String prefix = "";
if (methodName.startsWith("get")) {
prefix = "get";
} else if (methodName.startsWith("is")) {
prefix = "is";
}
if (StringUtils.isEmpty(prefix)) {
log.warn("无效的getter方法: " + methodName);
}
return lowerFirst(methodName.replace(prefix, ""));
}
public static String lowerFirst(String name) {
if (StringUtils.isEmpty(name)) {
return name;
}
name = name.substring(0, 1).toLowerCase() + name.substring(1);
return name;
}
/**
* 关键在于这个方法
*/
public static SerializedLambda getSerializedLambda(Serializable fn) {
SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
if (lambda == null) {
try {
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
lambda = (SerializedLambda) method.invoke(fn);
CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
} catch (Exception e) {
log.error("", e);
}
}
return lambda;
}
private static String toSnake(String fieldName) {
return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);
}
}
2.5 测试代码
public class Test {
public static void main(String[] args) {
//待校验的对象
User user = new User();
user.setAge(12);
user.setName("tom");
//对象的校验
ValidationResult validationResult = FunctionalValidatorExample.checkFrom(user)
.failOver()
.catchException()
.on(t -> t.getName().equals("libai"), "名字不符")
.onIf(t -> t.getGj().equals("china"), t -> t.getName().startsWith("王"), "不姓王")
.check();
//获取错误源
List sourceErrors = validationResult.getSourceErrors();
System.out.println(JSON.toJSONString(sourceErrors));
}
}
系列文章
设计模式那点事-万物皆可“建造者”