【问题标题】:How do I handle combinations of behaviours?如何处理行为组合?
【发布时间】:2009-06-30 09:42:31
【问题描述】:

我正在考虑验证各种格式的实数的问题,因为这与我在设计中面临的问题非常相似。

实数可能有不同的格式组合,例如: 1.前面有/没有标志 2.带/不带小数点(如果没有小数点,那么小数位数可以事先约定好) 3. base 10 或 base 16

我们需要考虑每个组合,所以有 2x2x2=8 个组合。您可以看到,随着每个新条件的施加,复杂性呈指数级增长。

在 OO 设计中,您通常会为每种数字格式分配一个类(例如,在这种情况下,我们有 8 个类),并且每个类都有一个单独的验证函数。但是,对于每个新条件,您都必须将所需的课程数量增加一倍,这很快就会变成一场噩梦。

在过程编程中,您使用 3 个标志(即 has_sign、has_decimal_point 和 number_base)来识别您正在验证的实数的属性。您有一个用于验证的函数。在那里,您将使用标志来控制其行为。


// This is part of the validation function

if (has_sign) check_sign();

for (int i = 0; i < len; i++) { if (has_decimal_point) // Check if number[i] is '.' and do something if it is. If not, continue

if (number_base = BASE10)
    // number[i] must be between 0-9
else if (number_base = BASE16)
    // number[i] must be between 0-9, A-F

}

同样,复杂性很快就会失控,因为函数中充满了 if 语句和标志。

我确信您之前遇到过这种性质的设计问题——许多独立的差异会导致行为差异。我很想知道您是如何在不使代码完全无法维护的情况下实现解决方案的。

桥模式之类的东西会有帮助吗?

【问题讨论】:

    标签: c++ oop software-design procedural-programming


    【解决方案1】:

    在 OO 设计中,您通常会 为每个数字分配一个类 格式(例如,在这种情况下,我们有 8 类),每个类都有一个 单独的验证函数。

    不不不不不。最多,你会有一个表示 数字输入 的类型(以防 String 不成功); Real Number 的另一个(在大多数语言中,您会选择内置类型,但无论如何);和一个 Parser 类,该类具有获取数字输入并将其转换为实数的知识。

    更笼统地说,一种行为差异本身并不会自动映射到一个类。它可以只是一个类中的一个属性。最重要的是,行为应该被正交对待。

    如果(假设您编写自己的解析器)您可能有或没有符号、是否有小数点、是否有十六进制,那么您有三个独立的复杂性来源,这将是可以在某处找到三段代码,分别处理其中一个问题;但是在任何地方都无法找到 2^3 = 8 段以明确方式处理不同组合的不同代码。

    想象一下添加一个新选择:突然间,您记得数字可能有一个“e”(例如 2.34e10)并希望能够支持它。使用正交策略,您将拥有另一个独立的复杂性来源,即第四个。以你的策略,这8个病例会突然变成16个!显然是禁忌。

    【讨论】:

    • 谢谢。那么您是说您同意“程序方法”(使用 3 个 if...else 语句来控制行为,假设它们没有交织在一起)?我同意,随着独立的复杂性来源数量的增加,使用类方法将很快成为一场噩梦。
    • (1) 在某些时候有一个选择(针对每个不同的复杂性来源)。当然,这种选择可以通过“if”语句来完成;但是,是的,让他们保持独立。并不是说“if”是过程性的,而不是面向对象的——Eiffel,最纯粹的面向对象语言之一,有一个“if”语句:) 当然,其他选项是一个 switch(这是一个类似强大的“if”)和, when 选择对对象的身份至关重要,通过继承实现多态性。此外,在某些情况下,模板(通用性)可以作为一种选择,例如你可能有一个 std::vector 或一个 std::vector.
    • (2) 另外,在你提到的事情中,输入是十六进制的事实对我来说是最相关的。你会以十六进制还是十进制存储它?您将如何识别它是十六进制? (没有人知道“19”是否真的是十六进制;你必须事先知道)。考虑并考虑到这一点。
    【解决方案2】:

    我不知道您为什么认为 OO 解决方案会涉及每个数字模式的类。我的 OO 解决方案是使用正则表达式类。如果我是程序化的,我可能会使用标准库 strtod() 函数。

    【讨论】:

    • 对不起,我只是以数字验证器为例提出一个反问。 (我知道有标准函数可以转换实数!)我已经编辑了我的问题以使其更清楚。
    【解决方案3】:

    你要一个解析器,用一个:

    还有:http://en.wikipedia.org/wiki/Parser_generator

    现在我该如何处理这类问题的复杂性?好吧,如果可以的话,我会重新制定。

    在您的情况下,使用解析器生成器(或正则表达式)正在使用 DSL(域特定语言),这是一种更适合您正在处理的问题的语言。

    设计模式和 OOP 很有用,但绝对不是每个问题的最佳解决方案。

    【讨论】:

    • 对不起,我只是以数字验证器为例提出一个反问。 (我知道有标准函数可以转换实数!)我已经编辑了我的问题以使其更清楚。
    【解决方案4】:

    抱歉,因为我使用 vb,所以我所做的是一个基本函数,然后我结合了一个评估函数 所以我用我的方式伪造了代码

    function getrealnumber(number as int){ return  getrealnumber(number.tostring) }
    function getrealnumber(number as float){ return  getrealnumber(number.tostring) }
    function getrealnumber(number as double){ return  getrealnumber(number.tostring) }
    function getrealnumber(number as string){
    if ishex(){ return evaluation()}
       if issigned(){ return evaluation()}
       if isdecimal(){ return evaluation()}
     }
    

    等你来弄清楚如何做二进制和八进制

    【讨论】:

      【解决方案5】:

      你不会用锤子杀死苍蝇。

      我真的觉得使用面向对象的解决方案来解决您的问题实在是太过分了。仅仅因为你可以设计面向对象的解决方案,并不意味着你必须对你遇到的每一个问题都强制这样做。

      根据我的经验,几乎每次很难找到解决问题的 OOD 解决方案,这可能意味着 OOD 不合适。 OOD 只是一个工具,它本身不是上帝。它应该用于解决大规模问题,而不是您提出的问题。

      所以给你一个实际的答案(就像上面提到的那样):使用正则表达式,除此之外的每个解决方案都只是矫枉过正。

      如果您坚持使用 OOD 解决方案.... 那么,由于您提供的所有格式都是相互正交的,我认为没有必要为每种可能的组合创建一个类。我会为每种格式创建一个类,并通过每个格式传递我的输入,在这种情况下,复杂性将线性增长。

      【讨论】:

      • 感谢您的意见。我同意你所说的,但请注意我给出的例子只是一个例子。我的问题是另一个无法通过正则表达式解决的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-01
      • 2021-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多