在设计一个类时,是否应该将维持有效状态的逻辑合并到类中或类外?也就是说,属性应该在无效状态上抛出异常(即值超出范围等),还是应该在构造/修改类的实例时执行此验证?
答案 0 :(得分:13)
它属于班级。除了类本身(以及它委派给它的任何助手)之外,应该知道或关注决定有效或无效状态的规则。
答案 1 :(得分:4)
是的,属性应在设置时检查有效/无效值。这就是它的用途。
答案 2 :(得分:3)
无论外面的代码如何,都应该无法将类置于无效状态。这应该说清楚。
另一方面,它之外的代码仍然负责正确使用类,因此经常检查两次是有意义的。如果传递了他们不喜欢的东西,那么类的方法可能抛出ArgumentException
,并且调用代码应该确保通过使用正确的逻辑来验证输入等不会发生这种情况。
还有一些更复杂的情况,即系统中涉及不同的“客户”级别。一个例子是操作系统 - 应用程序以“用户模式”运行,并且应该无法将操作系统置于无效状态。但是驱动程序以“内核模式”运行并且完全能够破坏操作系统状态,因为它是负责实现应用程序使用的服务的团队的一部分。
这种双层布置可以在对象模型中出现;模型的“外部”客户端只能看到有效状态,而“内部”客户端(插件,扩展,附加组件)必须能够看到否则将被视为“无效”状态,因为他们在实施状态转换中可以发挥作用。无效/有效的定义因客户端所扮演的角色而异。
答案 3 :(得分:2)
一般来说,这属于类本身,但在某种程度上它还必须依赖于你对'valid'的定义。例如,考虑System.IO.FileInfo
类。如果引用不再存在的文件,它是否有效?怎么会知道?
答案 4 :(得分:1)
我同意@Joel。典型地,这可以在课堂上找到。但是,我不会让属性访问器实现验证逻辑。相反,我建议在持久化对象时调用持久层的验证方法。这允许您在单个位置本地化验证逻辑,并根据正在执行的持久性操作对有效/无效做出不同的选择。例如,如果您计划从数据库中删除对象,您是否关心它的某些属性是无效的?可能不是 - 只要ID和行版本与数据库中的版本相同,您就可以继续删除它。同样,您可能对插入和更新有不同的规则,例如,某些字段在插入时可能为null,但在更新时需要。
答案 5 :(得分:0)
取决于。
如果验证很简单,并且只能使用类中包含的信息进行检查,那么在大多数情况下将状态检查添加到类中是值得的。
然而,有时这样做是不可能或不可取的。
一个很好的例子是编译器。检查抽象语法树(AST)的状态以确保程序有效通常不是由属性设置器或构造器完成的。相反,验证通常由树访问者或某种“语义分析类”中的一系列相互递归方法完成。但是,在任何一种情况下,属性都会在设置值后很久才会得到验证。
此外,对于用于旧UI状态的对象,在设置无效值时抛出异常通常是一个坏主意(从可用性的角度来看)。对于使用WPF数据绑定的应用程序尤其如此。在这种情况下,您希望向客户显示某种无模式反馈,而不是抛出异常。
答案 6 :(得分:0)
类中的有效状态最好用类不变的概念表达。它是一个布尔表达式,必须适用于该类的对象才有效。
Design by Contract 方法表明,作为C类的开发人员,您应该保证类不变量成立:
这意味着,由于对象是封装(除非通过调用公共方法,否则没有人可以修改它),在输入任何公共方法或进入析构函数时也会满足不变量(使用析构函数的语言),如果有的话。
每个公共方法声明调用者必须满足的前置条件,以及每个公共方法结束时类将满足的后置条件。违反前提条件有效地违反了类的合同,因此它仍然可以是正确的,但如果在违反前提条件的情况下调用它,则不必以任何特定方式行事,也不必保持不变量。在没有来电者违规的情况下履行合同的类可以说是正确。
不同于正确但与之互补的概念(当然属于软件质量的多重因素)是健壮的概念。在我们的上下文中,一个健壮的类将检测何时调用其方法之一而不满足方法前置条件。在这种情况下,通常会抛出断言违例异常,以便调用者知道他已将其吹掉。
因此,在回答您的问题时,班级及其来电者都有义务作为班级合同的一部分。一个强大的类将检测合同违规和吐痰。正确的来电者不会违反合同。
属于代码库公共接口的类应该被编译为健壮的,而内部类可以被测试为健壮,但是在发布的产品中运行正确,没有前置条件检查。这取决于许多事情和was discussed elsewhere。
答案 7 :(得分:0)
该类确实应该保持有效的值。如果通过构造函数或通过属性输入它们并不重要。两者都应该拒绝无效值。如果构造函数参数和属性都需要相同的验证,您可以使用公共私有方法来验证属性和构造函数的值,也可以在属性中进行验证,并在设置时使用构造函数中的属性局部变量。我建议亲自使用通用的验证方法。
如果收到无效值,您的类应抛出异常。总而言之,良好的设计可以帮助减少这种情况发生的可能性。