在面向对象的控制台应用程序中输入验证,它属于哪里?

时间:2012-09-21 23:46:16

标签: c# oop validation encapsulation

设计问题......

我的任务是在C#中创建一个小型控制台应用程序,并希望遵循最佳的封装实践。

我有UI层,控制器/命令处理器层,业务规则层和数据访问层。

该应用程序允许用户创建记录,填写一些字段,然后将其插入到内存中以及其余记录。

假设其中一些字段的有效选项数量有限,那么验证用户输入是否符合其中一个选项的最合理位置在哪里?

设置一个存在于UI中的临时记录是好的设计,当我使用用户数据并检查记录类中的setter方法的返回值时填写它?我的记录类是否应该实现验证,而是验证用户输入是否与业务规则层中的选项匹配?

如果这会影响答案,那么应用程序就是用C#编写的。我目前的想法是使用记录类中的属性并在UI中具有临时记录,然后仅在用户输入与选项匹配时才在记录类中设置基础成员。然后,UI将使用get方法将用户输入与记录中的值进行比较,并在值不匹配时提示用户重新输入数据(该集合未成功。)

是否有更好的方法遵循更好的设计原则?

提前致谢。

2 个答案:

答案 0 :(得分:0)

如果我理解正确的布局,我会推荐这样的东西:

控制器/命令处理器将验证命令,确保它们遵循适当的语法。

业务规则层将确保输入的所有数据都是有效数据。在将数据输入记录之前,它将被验证。

如果您有数据连接命令,数据访问层将确保输入的连接信息有效。

答案 1 :(得分:0)

我主张加强您的应用程序的输入。您的业​​务规则应仅使用强类型,经过验证的数据。他们不需要弄乱UI的怪癖。

让我们看看假设情况的事实:用户必须输入汽车序列号​​。对序列号的限制是它有15个字符,前两个是“VF”。

您的UI正在接受来自用户的一系列字符,无论是来自TextBox还是控制台输入。在这个级别,将这个字符序列存储到string对象中是有意义的,因为它就是这样的:一系列字符。

相反,在业务规则方面,它应该已经过验证。实际上,BR中只有一部分有效string感兴趣。因此,您的BR应该操纵另一种不是string的类型。 C#中的用户定义类型是classstruct

所以让我们写这种类型。我选择创建一个class,因为句法警告较少。

class CarSerialNumber {
}

汽车序列号​​在语义上是一个值,而不是一个对象。它具有价值语义。它的值可以存储到string中,我们必须重新定义Equals(object),GetHashCode(),它本身是等价的,所以它可以实现IEquatable。

class CarSerialNumber : IEquatable<CarSerialNumber> {
    string _value;

    public override bool Equals(object right) {
        if(this == right) {
            return true;
        }
        if(right == null) {
            return false;
        }

        if(!right is CarSerialNumber) {
            return false;
        }

        return Equals((CarSerialNumber)right);
    }

    public override int GetHashCode() {
        return _value.GetHashCode();
    }

    public bool Equals(CarSerialNumber right) {
        return _value == right._value;
    }
}

它应该用string值构造,并且是不可变的。

class CarSerialNumber : IEquatable<CarSerialNumber> {
    public CarSerialNumber(string value) {
        _value = value;
    }

    readonly string _value;

    // ... rest ....
}

在那里我们找到了铰链。此构造函数是从stringCarSerialNumber的转折点。此外,CarSerialNumber不应使用无效字符串构造。由于类型系统此时无法保证参数string是有效的汽车序列号​​,因此构造函数必须以异常进行挽救。

因此,验证属于RIGHT HERE。

class CarSerialNumber : IEquatable<CarSerialNumber> {
    public CarSerialNumber(string value) {
        Validate(value);
        _value = value;
    }

    private Validate(string value) {
        if(value == null) { // Never forget this one ;)
            throw new ArgumentNullException();
        }

        // Rest of validation, throwing exceptions on failure.
    }

    // ... rest ....
}

您可能非常想考虑使用此类型sealed。此类类型的继承可能包括其他陷阱。 (就像不相容的亚型的神奇等同性一样)

创建此类“类型更简单的类型”的其他优点是:

  • 当用作参数类型时,它会记录函数所需的内容,优于参数名称。 (或者至少,它是一个补充文件)
  • 以这种方式创建的类型彼此不兼容。如果您尝试将CarModel传递给期望CarSerialNumber的函数,编译器将发出警告。

更强类型的其他解决方案包括在编译时知道所有可能的值时使用enum