如何更好地减少类依赖性,以便更容易对它们进行单元测试

时间:2010-11-20 04:09:17

标签: c# java unit-testing oop dependencies

我正在开发一个遗传算法框架,最初决定了以下IIndividual定义:

public interface IIndividual : ICloneable
{
    int NumberOfGenes { get; }
    double Fitness { get; }
    IGene GetGeneAt(int index);
    void AddGene(IGene gene);
    void SetGeneAt(int index, IGene gene);
    void Mutate();
    IIndividual CrossOverWith(IIndividual individual);
    void CalculateFitness();
    string ToString();
}

看起来没问题,但是一旦我开发了其他使用IIndividual的类,我就得出结论,对这些类进行单元测试会有点痛苦。为了理解原因,我将向您展示IIndividual

的依赖关系图

alt text

因此,在使用IIndividual时,我最终还必须创建/管理IGeneIInterval的实例。 我可以轻松解决问题,将IIndividual界面重新定义为以下内容:

public interface IIndividual : ICloneable
{
    int NumberOfGenes { get; }
    void AddGene(double value, double minValue, double maxValue);
    void SetGeneAt(int index, double value);
    double GetGeneMinimumValue(int index);
    double GetGeneMaximumValue(int index);
    double Fitness { get; }
    double GetGeneAt(int index);
    void Mutate();
    IIndividual CrossOverWith(IIndividual individual);
    void CalculateFitness();
    string ToString();
}

使用以下依赖关系图:

alt text

这将非常容易测试,代价是性能下降(我此刻并不担心这一点)并且IIndividual更重(更多方法)。对于IIndividual的客户来说也存在一个很大的问题,就好像他们想要添加一个Gene一样,他们必须在AddGene(value, minimumValue, maximumValue)中“手动”添加Gene的所有小参数,而不是AddGene(gene)

我的问题是:

您更喜欢哪种设计?为什么?另外,知道在哪里停止的标准是什么?

我可以对IIndividual IGene做同样的事情,因此使用IGene的任何人都不必知道Interval。 我有一个Population课,它将作为IIndividuals的集合。是什么阻止了我对IIndividualPopulation做同样的事情?必须有某种边界,某种标准要理解哪些情况最好只是让它(有一些依赖),在哪种情况下最好隐藏它们(就像在第二个IIndividual实现中一样)。

此外,当实现一个应该由其他人使用的框架时,我觉得第二个设计不那么漂亮(并且其他人可能更难理解)。

谢谢!

2 个答案:

答案 0 :(得分:2)

@andersoj让你在正确的轨道上看到了Feather的书,但这里有一些更深入的分析:

Eric Evans 域驱动设计解决了您正在思考的边界类型。 free InfoQ summarization是一个很好的起点,因为埃文斯在我的拙见中过于冗长。埃文斯所描述的聚合根是我认为你需要考虑的边界类型

个体看起来像聚合根,暗示Genes和Intervals只存在于Individual类中,并且只能由Individual类创建。假设这些类不需要客户端使用,那么重构Individual类以有效隐藏或删除其他两个类是有道理的。

另一方面,如果客户确实需要独立于个体访问基因和区间,我建议考虑使它们不可变,因此不能隐含地改变个体的状态。

在任何一种情况下,都要从表征测试开始。这是捕获代码正在执行的测试现在。然后从那里重构。

答案 1 :(得分:1)

(我为没有直接回答你的问题而道歉,但我不能不这样指出你......) 我不能高度推荐这本书Working Effectively with Legacy Code(作者Michael Feathers)。对于在(单元,功能)测试下获取代码的挑战,它是一个出色的方法。