12

假设我有一个如下所示的类:

internal class SomeClass
{
    IDependency _someDependency;

    ...


    internal string SomeFunctionality_MakesUseofIDependency()
    {
    ...
    }
}

然后我想添加相关但使用不同依赖项来实现其目的的功能。也许类似于以下内容:

internal class SomeClass
{
    IDependency _someDependency;

    IDependency2 _someDependency2;

    ...


    internal string SomeFunctionality_MakesUseofIDependency()
    {
    ...
    }

    internal string OtherFunctionality_MakesUseOfIDependency2()
    {
    ...
    }
}

当我为这个新功能编写单元测试(或更新我对现有功能的单元测试)时,我发现自己创建了一个新的 SomeClass 实例(SUT),同时为我不需要的依赖项传入 null对于我要测试的特定功能。

这对我来说似乎是一种难闻的气味,但我发现自己走上这条路的真正原因是因为我发现自己为我引入的每一个新功能都创建了新的类。这似乎也是一件坏事,所以我开始尝试将类似的功能组合在一起。

我的问题:一个类的所有依赖项是否应该被它的所有功能所消耗,即如果不同的功能位使用不同的依赖项,那么这些可能应该存在于单独的类中是一个线索吗?

4

6 回答 6

11

当每个实例方法都接触到每个实例变量时,该类就具有最大的内聚性。当没有实例方法与任何其他实例方法共享实例变量时,该类具有最小内聚性。虽然我们确实喜欢高凝聚力,但也确实适用 80-20 规则。获得最后一点凝聚力可能需要付出巨大的努力。

通常,如果您有不使用某些变量的方法,那是一种气味。但是一点点气味并不足以完全重构类。这是值得关注和关注的事情,但我不建议立即采取行动。

于 2009-07-14T16:13:26.387 回答
3

SomeClass 是保持内部状态,还是只是“组装”各种功能?你可以这样重写它:

internal class SomeClass
{
    ...


    internal string SomeFunctionality(IDependency _someDependency)
    {
    ...
    }

    internal string OtherFunctionality(IDependency2 _someDependency2)
    {
    ...
    }
}

在这种情况下,如果 SomeFunctionality 和 OtherFunctionality 以某种方式(功能上)相关(使用占位符不明显),您可能不会破坏 SRP。

并且您具有能够从客户端选择要使用的依赖项的附加价值,而不是在创建/DI 时。也许为这些方法定义用例的一些测试将有助于澄清情况:如果您可以编写一个有意义的测试用例,其中两个方法都在同一个对象上调用,那么您就不会破坏 SRP。

至于 Facade 模式,我已经看过太多次疯狂的喜欢它了,你知道,当你最终得到一个 50 多个方法类时......问题是:你为什么需要它?出于效率原因,老旧 EJB?

于 2009-07-14T06:27:44.177 回答
0

如果方法使用可以封装在类中的共享状态,我通常将它们分组到类中。具有并非由类中的所有方法使用的依赖项可能是一种代码异味,但不是很强烈。我通常只在类变得太大、类有太多依赖项或方法没有共享状态时才从类中分离出方法。

于 2009-07-13T09:41:39.907 回答
0

我的问题:一个类的所有依赖项是否应该被它的所有功能所消耗,即如果不同的功能位使用不同的依赖项,那么这些可能应该存在于单独的类中是一个线索吗?

这是一个提示,表明你的类可能有点不连贯(“做的不仅仅是一件事”),但就像你说的,如果你走得太远,你最终会为每一个新功能创建一个新类. 因此,您可能希望引入外观对象以将它们再次组合在一起(看起来外观对象与此特定设计规则完全相反)。

您必须找到适合您(以及团队其他成员)的良好平衡。

于 2009-07-13T09:42:14.073 回答
0

对我来说似乎超载了。您正在尝试做某事,并且有两种方法可以做到,一种方法或另一种方法。在 SomeClass 级别,我将有一个依赖项来完成工作,然后让该单个依赖类支持两种(或更多)方法来做同样的事情,最有可能使用互斥的输入参数。换句话说,我将拥有与 SomeClass 相同的代码,但将其定义为 SomeWork,而不包含任何其他不相关的代码。

高温高压

于 2009-07-13T16:20:24.350 回答
0

当您想要隐藏复杂性(如遗留系统的接口)或想要整合功能同时从接口的角度向后兼容时,可以使用 Facade。

您的案例的关键是为什么您在同一个类中有两种不同的方法。是否有一个将相似类型的行为组合在一起的类的意图,即使它是通过不相关的代码实现的,例如在聚合中。或者,您是否尝试支持相同的行为,但根据具体情况有替代实现,这将暗示继承/重载类型的解决方案。

问题将是这个类是否会继续增长以及朝什么方向增长。两种方法没有区别,但如果重复超过 3 个,您将需要决定是要将其声明为外观/适配器,还是需要为变体创建子类。

您的怀疑是正确的,但气味只是燃烧的余烬中的一缕烟雾。您需要密切关注它,以防它突然爆发,然后您需要决定如何在火势失控之前将其扑灭。

于 2009-07-14T14:23:37.947 回答