单一责任原则:一个类中的所有公共方法都必须使用所有类依赖关系吗?

时间:2009-07-13 09:26:05

标签: class-design single-responsibility-principle

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

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为依赖我不需要我想要测试的特定功能。

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

我的问题:如果一个类的所有依赖关系都被它的所有功能所消耗,即如果不同的功能位使用不同的依赖关系,那么它们是否应该存在于不同的类中?

6 个答案:

答案 0 :(得分:11)

当每个实例方法接触每个实例变量时,该类最大程度地具有内聚性。当没有实例方法与任何其他方法共享实例变量时,该类具有最小的内聚性。虽然我们确实希望凝聚力很高,但80-20规则也适用。获得最后一点凝聚力的增加可能需要一定的努力。

一般情况下,如果您的方法不使用某些变量,那就是气味。但是一个小气味不足以完全重构这个阶级。这是值得关注和关注的事情,但我不建议立即采取行动。

答案 1 :(得分:3)

SomeClass是维护内部状态,还是只是“组装”各种功能?你能用这种方式重写它吗:

internal class SomeClass
{
    ...


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

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

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

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

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

答案 2 :(得分:0)

我通常将方法分组为类,如果它们使用可以封装在类中的共享状态块。具有未被类中的所有方法使用的依赖性可以是代码气味但不是非常强的。当类太大,类有太多依赖或方法没有共享状态时,我通常只从类中拆分方法。

答案 3 :(得分:0)

  

我的问题:如果一个类的所有依赖关系都被它的所有功能所消耗,即如果不同的功能位使用不同的依赖关系,那么它们是否应该存在于不同的类中?

这是一个提示,表明你的课程可能有点语无伦次(“做的不仅仅是一件事”),但就像你说的,如果你把这个放得太远,你最终会得到一个新的课程新功能。因此,您需要引入外观对象以将它们再次拉到一起(看起来外观对象与此特定设计规则完全相反)。

你必须找到一个适合你(和团队其他成员)的良好平衡。

答案 4 :(得分:0)

看起来像重载给我。 你正试图做某事,而且有两种方法可以做到这一点,无论如何。在SomeClass级别,我有一个依赖项来完成工作,然后让那个单独的依赖类支持两种(或更多)方法来做同样的事情,很可能是互斥的输入参数。 换句话说,我有与SomeClass相同的代码,但是将其定义为SomeWork,而不是包含任何其他不相关的代码。

HTH

答案 5 :(得分:0)

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

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

问题在于这门课程是否会继续发展并朝着什么方向发展。两种方法没有区别,但如果重复次数超过3,则需要决定是将其声明为外观/适配器还是需要为变体创建子类。

你的怀疑是正确的,但气味只是燃烧的灰烬中的一缕烟雾。你需要密切关注它,以防它爆炸,然后你需要做出决定,如果你想要在它失去控制之前解决火灾。