这种方法属于哪里?

时间:2009-04-21 17:59:56

标签: oop

假设我有一个从UI输入的模型地址。我必须验证地址永远不会被保存到处于不完整状态的系统中(无论这对业务可能意味着什么)。

当用户在UI中输入地址时,它会被序列化为Address对象。 所以我想知道:像“isComplete”这样的方法属于哪里?验证器或模型?

如果我将该方法放在地址模型中并在保存之前在验证器中调用address.isComplete(),则意味着不完整的地址是有效的系统状态;如果我在验证器中进行完整性检查,感觉验证器对地址内部有太多的了解。

我想知道,其他人遵循什么样的惯例?

编辑: 正如下面提到的那样,在上面的例子中,地址对象在多个系统中重用,并且一个系统中的“完整”对象可能不代表另一个系统中的“完整”对象。因此,没有一致的方法强制执行“永远不应该在无效状态中构造对象”,因为无效状态是依赖于上下文的。

11 个答案:

答案 0 :(得分:1)

听起来你需要一个AddressFactory,你传入一个地址的数据,它会给你一个有效的地址,或一个错误(异常/无论如何)。您可以确定创建无效地址并不理想。

如果这样做,您应该确定谁可以创建Address对象,以及是否可以在不使用AddressFactory的情况下创建它们。如果是这样,则Address对象本身需要防止无效输入,并且具有有效性检查的Address对象本身的参数变得更强。

答案 1 :(得分:1)

验证者,有两个原因。

首先,地址中几乎没有固有的行为 - 它只是数据。

其次,因为验证可能会根据地址的使用而改变。您可能有一条规则,在运送大件物品时禁止邮政信箱,但不关心帐单邮寄地址。对于帐单邮寄地址,您可能需要进行更严格的检查,以便在有人输入虚假地址时您无需支付付款网关。


回复评论:BillingAddress和ShippingAddress之间的数据有何不同?或者规则会改变吗?这些规则是否一致,或将来可能会改变(即UKBillingAddress,USBillingAddress)?

我同意在某些情况下让类型系统强制执行数据规则是有用的(测量是一个很好的例子; Google用于火星探测器,它在一个地方使用了米,在另一个地方使用了英尺)。但是,我怀疑地址不是其中之一。

答案 2 :(得分:1)

我为我使用的每种数据创建了一个数据类。除了那些数据类之外,我不使用“string”和“int”类型。我的数据类具有有意义的名称,并且在类的特定约束有意义的上下文中使用。

例如,如果您的“得分”字段仅对1到100之间的值有意义,那么您将 NO BUSINESS 存储在“int”类中,其范围为 - 2,147,483,648至2,147,483,647。如果你这样做,尝试在你传递它的任何地方冗余地验证它,并乐趣找出你需要验证它的时间和地点,并在忘记一个地点并让无效数据潜入你的系统时获得乐趣,并拥有它试图向我解释一下如何在为确定性数字硬件编写的软件中出现程序员错误的错误。

何时/何处验证:

如果您要接受用户界面中文本字段的分数,请在输入点对其执行初步验证,并提供适合该特定用户界面的错误消息。初步验证可能非常复杂,因为您需要来向用户提供有意义的反馈。

完成初始验证后,将数据填入适当有意义的数据类,如“Score”,构造函数将执行最终/权威验证。这种权威验证应尽可能简单有效,但绝对足以保证价值有效。这是一个布尔验证;数据有效或抛出适合程序员阅读的异常。例如,如果数据类的验证代码只是正则表达式匹配,请考虑在错误消息中包含数据类名称,正则表达式字符串和数据本身。所有基本数据类都应该是不可变的,就像字符串类本身一样;一旦建成,它保证有效并保持不变。

您的初步验证码可能会也可能不会利用数据类的验证码。这取决于您收集的数据的详细程度,以及您希望向UI发送的反馈量。类似地,您创建的包含数据类作为字段的更复杂的类不应该是可构造的,除非它们是有效的。因此,您可能需要编写验证或尝试构造每个单独数据字段的代码,捕获低级构造错误,并通过向UI提供更合适的反馈来处理它们。有时,此反馈可能涉及将字段着色为红色并在其旁边显示错误消息。正如您所看到的,有两种验证,其中一种显然更加复杂。

答案 3 :(得分:0)

如果您不希望验证器知道它的验证内容,您可以使用接口或控制模式的反转。在界面方面,它很容易......

interface IValidateable  // or whatever :)
{
   bool IsValid { get; }
}

这样,验证器只知道它需要询问一个对象是否有效,对该对象的内部状态一无所知。

IoC side上,这更复杂。您可以将验证器传递给它在对象上调用的特定方法签名(即验证我,调用它)。但是,这是一个更难解决的问题。

答案 4 :(得分:0)

你可以看一下复合材料。有一个基本的Validator,然后再分类到一个特定的AddressValidator。现在,验证者知道地址的工作方式是有意义的,但不要担心其他任何项目的验证方式。

然后,将许多验证器分组到ValidatorCollection中,并调用它的.Validate()方法,并在所有子节点上调用它们。

答案 5 :(得分:0)

isComplete()是检查还是命令?

如果是检查函数(if(isComplete()){})则它属于验证器。由于你有一个UI和一个模型,我假设你有一个控制器,可以在两者之间进行协调。在这种情况下,只需在调用保存例程之前从验证器调用您的检查功能,该例程会将您的模型标记为完成。

如果isComplete()是一个将数据标记为完整的命令,那么我认为你应该改变设计的那一部分。我的口味太乱了。

答案 6 :(得分:0)

IMO地址模型应该包含地址是完整还是不完整的信息。

让我们说,以后如果业务逻辑发生变化,现在不完整意味着至少5个字符。那么为什么验证者应该担心这种逻辑变化呢?。

我觉得,对象应该保持他们的数据和状态。

答案 7 :(得分:0)

我认为你所建议的一切都是相关的。有几种方法可以解决这个问题:

使验证器成为一个接口:

这可以像

那样完成
Validator addressValidator = new AddressValidator();
bool valid = addressValidator.validate(map);
if (valid) {
    Address address = addressValidator

通过这种方式,您可以识别addressValidator已耦合到Address。或者,您可以使用泛型

来完成此操作

将isComplete放入地址:

这是一个很好的oop启发式,可以说所有对象都应该用有效状态构造,但它并不总是成立。请记住,oop可以最大化您可以利用的编译时信息,即强制在编译时而不是在运行时发生许多错误。用户界面将在所有回合中阻止这一点。你有点不得不在某个地方违反这一点。 (我认为J2EE约定是构造没有参数的对象,例如调用一百个setter。)

将地址拆分为两个对象:

如果您想了解有效状态,那么您确实有两个不同的标准:UI返回的状态和模型所需的状态。为什么不明确区别?例如,如果我的UI有12个可用字段,我可以拥有一个包含12个字符串字段的UIAddress对象。我控制着这个:无论用户做什么,我都可以确保UI返回12个字段。然后,您可以创建UIAddress的实例并在其上调用isComplete()

UIAddress uiAddress = uiForm.getAddress();
if (uiAddress.isComplete()) {
    Address modelAddress = uiAddress.cleanedData();
}

这一切都取决于你的设计,但你真的有很多灵活性。恕我直言,“get-it-done”方式是将isComplete放入地址。

答案 8 :(得分:0)

恕我直言..你需要2级验证。

  • 客户端/用户界面级别:查找对更改具有低亲和力但易于更改并将其插入UI的规则。例如此字段的内容“必须为数字”,或者此字段的内容必须“匹配此模式”或“必需”。声明性规则存在于此处。用户立即反馈。
  • 模型级别:将其他所有内容移动到模型/业务规则中。任何可以改变的东西都应该放在这里。你无法取消模型验证,因为我可以通过绕过GUI的代码来实现这一点
Address a = new Address();
// set fields as I deem fit
a.Save();

总结:尝试标记问题并尽可能地排除错误输入而不打击模型。该模型是您的最后一道防线,并确保没有错误的输入通过它。

答案 9 :(得分:0)

如果这是一个数据输入应用程序,用户必须在保留任何内容之前完成整个表单/地址,那么Validator应该拥有isComplete方法,并且模型将永远不必处理除完整地址之外的任何内容

但是,如果用户可能需要保存部分结果并稍后继续(例如,如果包含地址的表单只是'向导'序列中可以中断/继续的一步),那么isComplete属于模型, 'incomplete'是地址的有效状态。

或者,isComplete方法可以属于Validator(在你的框架中意味着什么!)但是它的完整/不完整状态成为地址数据的一部分。< / p>

答案 10 :(得分:0)

嗯,我会考虑以下事项:

  1. 哪些域/子系统需要有效地址?
  2. 有多少这样的域名?
  3. 哪些域是可能存在的无效地址?
  4. 是否存在影响确定有效性的其他情况(验证来源是什么)。验证器是否是与需要有效地址的域不同的实体。 (它可以,例如是远程服务)

如果有多个域,每个域都要求不同的有效性定义,那么当地址传递给它们时,它们更有意义强制执行有效性,并假设遇到所有地址除非另有证明,否则无效。另一方面,如果对构成有效地址的内容只有一个单一的定义,则发起者在提供地址之前执行验证可能更有意义,并且假定它们是有效的。如果验证器是一个单独的(可插入的?)实体,它可以是一个单独的服务。