问题描述
我需要构建一个过程,该过程将根据约200个验证规则验证记录.记录可以是约10种类型之一.从验证规则到记录类型有一些细分,但是存在很多重叠,这使我无法清洁固定验证规则.
在我的设计期间,我正在考虑所有验证规则的责任模式.这是一个好主意还是有更好的设计模式?
推荐答案
验证通常是复合模式.当您分解时,您想将您想从中分开 您想做什么,您会得到:
如果foo有效 然后做点什么.
在这里,我们有抽象是有效的 - 警告:此代码是从Currrent抬起的,类似的示例,因此您可能会发现缺失的符号等.但这就是您得到图片.另外,
Result
对象包含有关故障以及简单状态(true/false)的消息传递. 这使您可以选择"它通过了?" vs."如果失败了,请告诉我为什么"
QuickCollection
和
QuickMap
是参加任何课程的便利课程,并通过仅分配给委托来快速将它们变成那些受人尊敬的类型.对于此示例,这意味着您的复合验证器已经是一个集合,可以迭代.
.您的问题中有一个次要问题:"干净地绑定",如" type a" - > ulud {a,b,c}"和" type b" - > ulud strual {c,e,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);
其他推荐答案
责任链意味着必须进行验证的顺序.我可能会使用类似于策略模式的东西,在该模式中,您拥有一组应用于特定类型记录的验证策略.然后,您可以使用工厂检查记录并应用正确的验证集.
问题描述
I need to build a process which will validate a record against ~200 validation rules. A record can be one of ~10 types. There is some segmentation from validation rules to record types but there exists a lot of overlap which prevents me from cleanly binning the validation rules.
During my design I'm considering a chain of responsibility pattern for all of the validation rules. Is this a good idea or is there a better design pattern?
推荐答案
Validation is frequently a Composite pattern. When you break it down, you want to seperate the what you want to from the how you want to do it, you get:
If foo is valid then do something.
Here we have the abstraction is valid -- Caveat: This code was lifted from currrent, similar examples so you may find missing symbology and such. But this is so you get the picture. In addition, the
Result
Object contains messaging about the failure as well as a simple status (true/false). This allow you the option of just asking "did it pass?" vs. "If it failed, tell me why"
QuickCollection
and
QuickMap
Are convenience classes for taking any class and quickly turning them into those respected types by merely assigning to a delegate. For this example it means your composite validator is already a collection and can be iterated, for example.
You had a secondary problem in your question: "cleanly binding" as in, "Type A" -> rules{a,b,c}" and "Type B" -> rules{c,e,z}"
This is easily managed with a Map. Not entirely a Command pattern but close
Map<Type,Validator> typeValidators = new HashMap<>();
Setup the validator for each type then create a mapping between types. This is really best done as bean config if you're using Java but Definitely use dependency injection
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(); } }
Now some simple implementations to show the point:
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(); } }
Here is another we will combine with
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(); } }
Now here is a composite:
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(); } } }
and now a snippet of use:
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);
其他推荐答案
Chain of responsibility implies that there is an order in which the validations must take place. I would probably use something similar to the Strategy pattern where you have a Set of validation strategies that are applied to a specific type of record. You could then use a factory to examine the record and apply the correct set of validations.