2013-05-14 30 views
9

我经常遇到这样的情况,我想通过传递一些给定的数据或可能是另一个对象来创建对象的实例,但数据或对象需要有效或处于正确的状态。我在做这件事的'正确'方式上总是有点不清楚。这是我的例子:什么设计模式用于验证数据和创建对象

鉴于这种类:

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 
     } 
    } 
} 

这是可能的,如果你这样做会碰到一些问题:

var setofdata = new SetOfData<SomeType>(); 

// if data is not valid then the object will be created incorrectly 
var businessObject = new BusinessObject(setofdata); 

所以我的解决方案一直是两种:

class BusinessObjectBuilder() 
{ 
    public BusinessObject Build(SetOfData<SomeType> setofdata) 
    { 
     // an example of some validation 
     if (setofdata.count > Threshold) 
      return new BusinessObject(setofdata); 
     } 
     else 
     { 
      return null; 
     } 
    } 
} 

或者使构造函数为private并添加静态工厂方法:

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 
    } 
} 

理想我不希望如果数据是无效的,因为有可能是在一个进程中创建多个业务对象,我不希望如果验证失败和追赶的全过程失败,并抛出一个异常抑制异常并不好。

另外,我读到的抽象工厂或工厂方法的所有示例都涉及传入某种类型或枚举以及正在构建和返回的正确对象。他们似乎从未涉及这种情况。

那么这种情况下的约定是什么?任何建议将不胜感激。

+0

只需注意,工厂不需要“传入某种类型或枚举”;他们可以采取任何类型的数据(甚至是“SetOfData ”)或根本没有数据(无参数)。这些示例倾向于使用它们,因为它是使用/描述它们的相当常见/简单的方式。如果你愿意的话,你总是可以为工厂创建一个'BusinessObjectValidator'来利用它来检查这些参数,但是如果检查如你所描述的那样简单,我就尽快将它放到工厂创建方法中。 – 2013-05-14 10:38:44

回答

11

恕我直言,构造验证是最适合很多情况下,你需要确保的是,除非被设置指定的参数没有对象可以被创建。

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"); 
     } 
    } 
} 

然而,这有不好的实现时:

  • 你可能有无效的对象,例如当处于草案状态,等
  • 用于ORM时默认的构造函数需要
  • 如果重验证逻辑发生。

对于第1和第2点,除了请求 - 验证 - 提交机制,我不能建议任何其他选项。

对于第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 
     } 
    } 
} 

这实施模块化编程并防止单片代码。

两个密码是:

  • 容易测试
  • 轻松浏览
  • 扩展
1

我不能告诉“正确”的方式。但我可以告诉你我的的方式=)

我会选择工厂模式。如果您不想因验证错误而中止应用程序,工厂可以使用默认值填充故障部件。

2

也许你可以实现Strategy PatternFactory (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; 
    } 
} 

,你需要现在创建许多不同validation classes,然后改变你的create方法:

public static Create(SetOfData<SomeType> setofdata, DataValidationStrategy validation) 
{ 
    if (validation.isValid(setofData)) 
    { 
     return new BusinessObject(setofdata); 
    } 
    else 
    { 
     return null; 
    } 
} 

编辑:另外,你可能会考虑使用prototypenull object而不是null的返回值。

+2

我相信在这种情况下空对象会是有害的。消费者可以检查null并假定如果返回的BusinessObject不为null,则它已正确创建。并检查由Create返回的对象是否为空对象将是不正确使用模式的一个示例。 – lisp 2013-05-14 21:36:09

1

我相信这是一般的做法是从像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(); 
} 

我还是离开异常抛出,因为没有保证setofdataCanBuild验证。这提供了避免使用流量控制异常的手段,但是具有验证两次的缺点。要消除双重验证,请将TryCreate放在Create旁边或替代Create。通过查看签名,您可以看到,除了创建业务对象之外,此方法还会执行输入数据验证,并以非异常形式返回验证结果。 (见int.Parseint.TryParse

public bool TryBuild(SetOfData<SomeType> setofdata, out BusinessObject businessObject) 
{ 
    if (validator.IsValid(setofdata)) 
    { 
     businessObject = new BusinessObject(setofdata); 
     return true; 
    } 
    businessObject = null; 
    return false; 
} 

此验证方法将调用构造函数无法访问其他不包含验证,而所有的广泛访问的构造函数仍然验证和抛出异常。

当然,方法BuiltTryBuilt可以是静态的(如在int中),或者您可以应用工厂模式。

0

看看OVal框架。你可以扩展它自己的注释。