java 用于验证数据和创建对象的设计模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16540583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What design pattern to use for validating data and creating an object
提问by Mark Vickery
I often come across situations where a I want to create an instance of an object by passing it some given data or maybe another object but the data or object needs to be valid or in the right state. I am always a bit unclear on the 'correct' way of doing this. Here is my example:
我经常遇到这样的情况,我想通过传递一些给定的数据或另一个对象来创建对象的实例,但数据或对象需要有效或处于正确的状态。我总是有点不清楚这样做的“正确”方式。这是我的例子:
Given this class:
鉴于这个类:
class BusinessObject()
{
const Threshold = 10;
public BusinessObject(SetOfData<SomeType> setofdata)
{
// an example of some validation
if (setofdata.count > Threshold)
{
// performance some business logic
// set properties
}
}
}
It is possible to run into some problems if you do this:
如果这样做,可能会遇到一些问题:
var setofdata = new SetOfData<SomeType>();
// if data is not valid then the object will be created incorrectly
var businessObject = new BusinessObject(setofdata);
So my solutions have always been either:
所以我的解决方案一直是:
class BusinessObjectBuilder()
{
public BusinessObject Build(SetOfData<SomeType> setofdata)
{
// an example of some validation
if (setofdata.count > Threshold)
return new BusinessObject(setofdata);
}
else
{
return null;
}
}
}
Or make the constructor private and add a static factory method:
或者将构造函数设为私有并添加静态工厂方法:
class BusinessObject()
{
const Threshold = 10;
public static Create(SetOfData<SomeType> setofdata)
{
if (setofdata.count > Threshold)
{
return new BusinessObject(setofdata);
}
else
{
return null;
}
}
private BusinessObject(SetOfData<SomeType> setofdata)
{
// performance some business logic
// set properties
}
}
ideally I would not like to throw an exception if the data is invalid as there might be multiple business objects being created in one process and I don't want the whole process to fail if one validation fails and catching and suppressing exceptions is not good.
理想情况下,如果数据无效,我不想抛出异常,因为在一个过程中可能会创建多个业务对象,如果一个验证失败并且捕获和抑制异常不好,我不希望整个过程失败。
Also all examples I read of the Abstract Factory or Factory method involve passing in some type or enum and a correct object being built and returned. They never seem to cover this scenario.
此外,我读到的所有抽象工厂或工厂方法的示例都涉及传入某种类型或枚举以及正在构建和返回的正确对象。他们似乎从未涵盖这种情况。
So what are the conventions in this scenario? Any advice would be greatly appreciated.
那么在这种情况下有哪些约定呢?任何建议将不胜感激。
回答by Fendy
IMHO constructor validation is the best for many situation where you need to make sure that no object can be created unless specified parameter being set.
恕我直言,构造函数验证最适合您需要确保除非设置指定参数才能创建任何对象的许多情况。
public class BusinessObject
{
const Threshold = 10;
public BusinessObject(SetOfData<SomeType> setofdata)
{
// an example of some validation
if (setofdata.count > Threshold)
{
throw new InvalidOperationException("Set data must be above treshold");
}
}
}
However, this has bad implementation when:
但是,在以下情况下这有不好的实现:
- You may have invalid object such as when in draft status, etc
- Used in ORM when default constructor needed
- If heavy validation logic occurs.
- 您可能有无效对象,例如处于草稿状态等
- 在需要默认构造函数时在 ORM 中使用
- 如果出现繁重的验证逻辑。
For point no.1 and 2, I cannot suggest any other option except request - validate - submit mechanism.
对于第 1 点和第 2 点,除了请求 - 验证 - 提交机制之外,我不能建议任何其他选项。
For point no.3, the reason is, the class will do too much for validation itself and creating a monolithic code. If there is much validation logic, I suggest to implement builder pattern with injected validator, and make the constructor of BusinessObject
internal.
对于第 3 点,原因是该类会为验证本身和创建整体代码做太多事情。如果验证逻辑比较多,我建议使用注入验证器来实现构建器模式,并将构造函数设为BusinessObject
内部。
public class BusinessObjectBuilder
{
public BusinessObjectBuilder(IBusinessObjectValidator validator){
this.validator = validator;
}
IBusinessObjectValidator validator;
public BusinessObject Build(SetOfData<SomeType> setofdata)
{
// an example of some validation
if (validator.IsValid(setofdata))
return new BusinessObject(setofdata);
}
else
{
throw new //exception
}
}
}
This enforce modular programming and prevent monolithic code.
这强制模块化编程并防止单体代码。
Both of the code is:
两个代码都是:
- easy to test
- easy to review
- extendable
- 易于测试
- 易于
- 可扩展的
回答by Marco Forberg
Maybe you could implement the Strategy Patternto your Factory (method)
to provide some validation capabilities:
也许您可以为您实现策略模式Factory (method)
以提供一些验证功能:
public interface DataValidationStrategy {
boolean isValid(SetOfData<SomeType> setofdata);
}
public class ThresholdValidation implements DataValidationStrategy {
private int threshold;
public ThresholdValidation(int threshold) {
this.threshold = threshold;
}
@Override
public booleam isValid(SetOfData<SomeType> setofdata) {
return setofdata.count > threshold;
}
}
Now create as many different validation classes
as you require and then change your create
method:
现在根据validation classes
需要创建尽可能多的不同,然后更改您的create
方法:
public static Create(SetOfData<SomeType> setofdata, DataValidationStrategy validation)
{
if (validation.isValid(setofData))
{
return new BusinessObject(setofdata);
}
else
{
return null;
}
}
Edit:In addition you might consider using a prototypeor a null objectinstead of a null
return value.
回答by desperateCoder
I cant tell the "correct" way. But I can tell you myway =)
我无法说出“正确”的方式。但我可以告诉你我的方式 =)
I would pick the factory-pattern. If you don't want to abort your application in cause of a validation error, the factory could fill the faulty parts with default-values.
我会选择工厂模式。如果您不想因验证错误而中止应用程序,工厂可以使用默认值填充有问题的部分。
回答by lisp
I believe it is general practice to throw an exception from a method like Create
when the arguments are incorrect. You are right in your reservations about returning null
in this scenario and trying to avoid using exceptions for flow control. You could simply have:
我相信一般的做法是从方法中抛出异常,例如Create
参数不正确时。您对在null
这种情况下返回并尝试避免使用异常进行流控制持保留态度是正确的。你可以简单地拥有:
public bool CanBuild(SetOfData<SomeType> setofdata)
{
return validator.IsValid(setofdata);
}
public BusinessObject Build(SetOfData<SomeType> setofdata)
{
if (validator.IsValid(setofdata))
{
return new BusinessObject(setofdata);
}
throw new ArgumentException();
}
I still leave the exception throwing, because there's no guarantee that setofdata
was validated with CanBuild
. This provides means of avoiding using exceptions for flow control, but has the drawback of validating twice. To eliminate the double validation, put TryCreate
next to or instead of Create
. Just by looking at the signature you see that, apart from creating the business object, this method performs validation of input data and returns the result of this validation in form other than an exception. (See int.Parse
and int.TryParse
)
我仍然会抛出异常,因为不能保证setofdata
使用CanBuild
. 这提供了避免使用异常进行流控制的方法,但存在验证两次的缺点。要消除双重验证,请放在TryCreate
旁边或代替Create
。仅通过查看签名,您就会发现,除了创建业务对象之外,此方法还执行输入数据的验证并以非异常形式返回此验证的结果。(见int.Parse
和int.TryParse
)
public bool TryBuild(SetOfData<SomeType> setofdata, out BusinessObject businessObject)
{
if (validator.IsValid(setofdata))
{
businessObject = new BusinessObject(setofdata);
return true;
}
businessObject = null;
return false;
}
This validating methods would call constructors inaccessible to others that contain no validation, while all widely accessible constructors would still validate and throw exception.
这种验证方法将调用其他不包含验证的构造函数无法访问,而所有可广泛访问的构造函数仍将验证并抛出异常。
Of course methods Built
and TryBuilt
can be static (as in int
) or you can apply a factory pattern.
当然方法Built
和TryBuilt
可以是静态的(如int
),或者您可以应用工厂模式。