代码之家  ›  专栏  ›  技术社区  ›  devoured elysium

需要在值类型的对象中使用随机数生成器,但不希望其依赖性负担过重

  •  3
  • devoured elysium  · 技术社区  · 14 年前

    我目前有一个称为 Gene ,只有两个字段:

    double value;
    Interval intervalOfAllowedValues;
    

    有时我需要一个 基因 将其值随机切换到其他值,只要它仍在中定义的范围内 intervalOfAllowedValues .

    我做了这个,一个特殊的方法

    public Gene RandomMutation() { ... }
    //it returns a Gene because this class is immutable!
    

    这样就可以解决问题,使用 INumberGenerator.GenerateDouble(...) . 问题是 RandomMutation() 接受 IRandomNumberGenerator 作为论据 基因 将不得不采取一个构造函数注入。

    这两种解决方案我都不喜欢:

    如果 随机变异() 通过参数接受数字生成器,这意味着现在不仅 基因 必须知道 INumberGenenerator 还有包含它的类。

    另一方面,如果我通过构造函数注入 INumberGenerator 为了这个 基因 ,大多数时候我会把它放在那里,而不去利用它,这看起来不太好。我觉得这有点违背了 基因 对象作为值类型存在。 它还提出了一个微妙的问题,即 基因 如果它们有不同的数字发生器,则s是相等的。如果是,如何进行单元测试?

    还有第三种选择:我接受 随机变异() 远离 基因 上课。现在的问题是包含 基因 必须同时知道 基因 关于 Interval ,这可能是不可取的。此外,行为应该接近其数据,而在遵循这种方法时,情况并非如此。

    还有第四个(!)方法:使数字生成器成为全局的(单例)。这会产生奇迹,但这违背了明确每一种依赖的哲学。

    你是怎么处理这种情况的?

    谢谢

    4 回复  |  直到 14 年前
        1
  •  1
  •   Bryan Watts    14 年前

    它不是一个评估依赖关系的有用指标 INumberGenerator 在一个特定的 Gene 实例。它只用于查看 定义 属于 基因 . 即使只使用一次,它也应该被当作一个一流的依赖项来对待。

    更大的问题是 struct 完全依赖。 RandomMutation 必须 有一个 发电机 函数,但无论您使用构造函数还是属性注入,任何人都可以调用 new Gene() 并创建具有空依赖项的实例。您根本无法强制将依赖项提供给 结构 .

    你有两个选择。首先是定义 基因 作为一个 class ,这将允许您申报 发电机 作为通过构造函数参数的必需依赖项。这是面向对象的答案。

    你的第二个选择是使用更实用的方法 基因 作为一个 结构 并在外部定义功能。函数式程序倾向于将数据结构与作用于它们的逻辑分离(参见LINQ),而OO则致力于将行为和数据结合起来。你的问题听上去最好是用这种方式建模。

    但核心问题仍然存在,那就是你需要 基因 还有一个 发电机 为了随机变异。因为我们已经确定 基因 可能不该知道 发电机 可以直接将其建模为扩展方法:

    public static Gene Mutate(this INumberGenerator generator, Gene gene)
    {
        // ...
    }
    

    这需要 发电机 依赖关系超出 基因 然后把它泡泡到知道基因什么时候应该变异的物体上。

        2
  •  3
  •   jrcalzada    14 年前

    我认为你的第三个选择肯定是可行的,不仅是因为可测试性,还因为一般的设计考虑。最后,基因是一种传输数据的方式,但它本身并没有真正的行为。突变只是发生在数据上的事情。因此,随机指令在其他地方仍然有能力对基因起作用是有意义的。

    在可测试性方面,这也是迄今为止最干净的方法,它可以保持所有内容都是明确的,并且在必要时可以很容易地替换为stubing。

        3
  •  3
  •   Dmitri    14 年前

    我认为有一个GeneMutator类没有什么错,它有一个接受基因并返回突变基因的mutate方法(并保持对INumberGenerator的引用)。

    Interval 似乎很普通,我不认为它的缺点是在 Gene .

        4
  •  2
  •   Phil Sandler    14 年前

    如果可测试性是你的目标,我喜欢你的第三个选择。在我看来,让行为接近数据是次要目标,而可测试性和SRP是主要目标。

    我的第二个选择是使用构造器注入将服务放入Gene(或者setter注入,尽管我不是一个粉丝)。你的主要反对意见似乎是“大多数时候,我会在不使用它的情况下使用它,这看起来不太好。”这也适用于直接在类上使用该方法——不同的是,你只是将该功能移到一个注入的类上。