用于验证数据和创建对象的设计模式

时间:2013-05-14 10:18:04

标签: c# java design-patterns

我经常遇到这样的情况:我想通过传递一些给定的数据或者另一个对象来创建对象的实例,但数据或对象需要有效或处于正确的状态。我总是对“正确”的做法有点不清楚。这是我的例子:

鉴于此课程:

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

或者将构造函数设为私有并添加静态工厂方法:

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

理想情况下,如果数据无效,我不想抛出异常,因为在一个进程中可能会创建多个业务对象,如果一个验证失败并且捕获和抑制异常,我不希望整个进程失败不好。

我读取的Abstract Factory或Factory方法的所有示例都涉及传入某些类型或枚举以及正在构建和返回的正确对象。他们似乎永远不会涵盖这种情况。

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

5 个答案:

答案 0 :(得分:11)

对于许多情况,IMHO构造函数验证是最好的,除非设置了指定的参数,否则您需要确保不能创建任何对象。

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 Pattern实施到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;
    }
}

现在根据需要创建尽可能多的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 :(得分:1)

我无法说出“正确”的方式。但我可以告诉你我的 way =)

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

答案 3 :(得分: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();
}

我仍然抛出异常抛出,因为无法保证setofdata已通过CanBuild验证。这提供了避免使用流控制异常的方法,但具有两次验证的缺点。要取消双重验证,请将TryCreate放在Create旁边或代替int.Parse。只需查看您看到的签名,除了创建业务对象之外,此方法还会执行输入数据的验证,并以异常以外的形式返回此验证的结果。 (请参阅int.TryParsepublic bool TryBuild(SetOfData<SomeType> setofdata, out BusinessObject businessObject) { if (validator.IsValid(setofdata)) { businessObject = new BusinessObject(setofdata); return true; } businessObject = null; return false; }

Built

这种验证方法会调用不包含任何验证的其他构造函数,而所有可广泛访问的构造函数仍会验证并抛出异常。

当然,方法TryBuiltint可以是静态的(如{{1}}中所示),也可以应用工厂模式。

答案 4 :(得分:0)

查看OVal框架。您可以使用自己的注释扩展它。