对于大型验证任务,责任链模式是一个不错的选择吗?

我需要构建一个过程,该过程将根据大约200个验证规则验证记录。一条记录可以是大约 10 种类型之一。从验证规则到记录类型有一些细分,但存在很多重叠,这使我无法干净利落地将验证规则装箱。

在我的设计过程中,我正在考虑所有验证规则的责任链模式。这是一个好主意还是有更好的设计模式?


答案 1

验证通常是复合模式。当你分解它时,你想把你想要的东西和你想做的事情分开,你会得到:

如果 foo 是有效的,那就做点什么。

在这里,我们有抽象是有效的 - 警告:此代码是从当前,类似的示例中移除的,因此您可能会发现缺少的符号系统等。但这是为了让你明白这一点。此外,

Result

对象包含有关失败的消息以及简单状态(真/假)。这使您可以选择只询问“它通过了吗?”与“如果它失败了,告诉我为什么”

QuickCollection

QuickMap

是方便的类,用于学习任何类,并通过仅分配给委托来快速将它们转换为那些受人尊敬的类型。例如,对于此示例,这意味着您的复合验证程序已经是一个集合,可以进行迭代。

你的问题中有一个次要问题:“干净地绑定”,如“类型A”->规则{a,b,c}“和”B类型“ ->规则{c,e,z}”

这可以通过地图轻松管理。不完全是命令模式,但关闭

Map<Type,Validator> typeValidators = new HashMap<>();

为每个类型设置验证程序,然后在类型之间创建映射。如果您使用的是Java,那么最好作为bean配置完成,但绝对要使用依赖注入

    public interface Validator<T>{
    
    
        public Result validate(T value);
    
    
        public static interface Result {
    
            public static final Result OK = new Result() {
                @Override
                public String getMessage() {
                    return "OK";
                }
    
                @Override
                public String toString() {
                    return "OK";
                }
    
                @Override
                public boolean isOk() {
                    return true;
                }
            };
    
            public boolean isOk();
    
            public String getMessage();
        }
    }

现在一些简单的实现来说明这一点:

public class MinLengthValidator implements Validator<String> {
    
    private final SimpleResult FAILED;
    
    private Integer minLength;
    
    public MinLengthValidator() {
        this(8);
    }
    
    public MinLengthValidator(Integer minLength) {
        this.minLength = minLength;
        FAILED = new SimpleResult("Password must be at least "+minLength+" characters",false);
    }
    
    @Override
    public Result validate(String newPassword) {
        return newPassword.length() >= minLength ? Result.OK : FAILED;
    }
    
    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }
}

这是另一个我们将结合

    public class NotCurrentValidator implements Validator<String> {
    
        @Autowired
        @Qualifier("userPasswordEncoder")
        private PasswordEncoder encoder;
    
        private static final SimpleResult FAILED = new SimpleResult("Password cannot be your current password",false);
    
        @Override
        public Result validate(String newPassword) {
            boolean passed = !encoder.matches(newPassword,user.getPassword());
            return (passed ? Result.OK : FAILED);
        }
    
        @Override
        public String toString() {
            return this.getClass().getSimpleName();
        }
    
    }

现在这是一个复合:

public class CompositePasswordRule extends QuickCollection<Validator> implements Validator<String> {


public CompositeValidator(Collection<Validator> rules) {
    super.delegate = rules;
}

public CompositeValidator(Validator<?>... rules) {
    super.delegate = Arrays.asList(rules);
}



@Override
public CompositeResult validate(String newPassword) {
    CompositeResult result = new CompositeResult(super.delegate.size());
    for(Validator rule : super.delegate){
        Result temp = rule.validate(newPassword);
        if(!temp.isOk())
            result.put(rule,temp);
    }

    return result;
}


    public static class CompositeResult extends QuickMap<Validator,Result> implements Result {
        private Integer appliedCount;

        private CompositeResult(Integer appliedCount) {
            super.delegate = VdcCollections.delimitedMap(new HashMap<PasswordRule, Result>(), "-->",", ");
            this.appliedCount = appliedCount;
        }

        @Override
        public String getMessage() {
            return super.delegate.toString();
        }

        @Override
        public String toString() {
            return super.delegate.toString();
        }

        @Override
        public boolean isOk() {
            boolean isOk = true;
            for (Result r : delegate.values()) {
                isOk = r.isOk();
                if(!isOk)
                    break;
            }
            return isOk;
        }
        public Integer failCount() {
            return this.size();
        }

        public Integer passCount() {
            return appliedCount - this.size();
        }
    }
}

现在是一个使用片段:

private Validator<String> pwRule = new CompositeValidator<String>(new MinLengthValidator(),new NotCurrentValidator());

Validator.Result result = pwRule.validate(newPassword);
if(!result.isOk())
    throw new PasswordConstraintException("%s", result.getMessage());

user.obsoleteCurrentPassword();
user.setPassword(passwordEncoder.encode(newPassword));
user.setPwExpDate(DateTime.now().plusDays(passwordDaysToLive).toDate());
userDao.updateUser(user);

答案 2

责任链意味着必须按顺序进行验证。我可能会使用类似于 Strategy 模式的东西,在这种模式中,您有一组应用于特定类型记录的验证策略。然后,您可以使用工厂来检查记录并应用正确的验证集。


推荐