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

发布于 2024-07-27 15:42:52 字数 843 浏览 8 评论 0原文

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

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

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

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

Say I have a class that looks like the following:

internal class SomeClass
{
    IDependency _someDependency;

    ...


    internal string SomeFunctionality_MakesUseofIDependency()
    {
    ...
    }
}

And then I want to add functionality that is related but makes use of a different dependency to achieve its purpose. Perhaps something like the following:

internal class SomeClass
{
    IDependency _someDependency;

    IDependency2 _someDependency2;

    ...


    internal string SomeFunctionality_MakesUseofIDependency()
    {
    ...
    }

    internal string OtherFunctionality_MakesUseOfIDependency2()
    {
    ...
    }
}

When I write unit tests for this new functionality (or update the unit tests that I have for the existing functionality), I find myself creating a new instance of SomeClass (the SUT) whilst passing in null for the dependency that I don't need for the particular bit of functionality that I'm looking to test.

This seems like a bad smell to me but the very reason why I find myself going down this path is because I found myself creating new classes for each piece of new functionality that I was introducing. This seemed like a bad thing as well and so I started attempting to group similar functionality together.

My question: should all dependencies of a class be consumed by all its functionality i.e. if different bits of functionality use different dependencies, it is a clue that these should probably live in separate classes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

宛菡 2024-08-03 15:42:52

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

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

When every instance method touches every instance variable then the class is maximally cohesive. When no instance method shares an instance variable with any other, the class is minimally cohesive. While it is true that we like cohesion to be high, it's also true that the 80-20 rule applies. Getting that last little increase in cohesion may require a mamoth effort.

In general if you have methods that don't use some variables, it is a smell. But a small odor is not sufficient to completely refactor the class. It's something to be concerned about, and to keep an eye on, but I don't recommend immediate action.

寻找一个思念的角度 2024-08-03 15:42:52

SomeClass 是否维护内部状态,或者只是“组装”各种功能? 您可以这样重写吗:

internal class SomeClass
{
    ...


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

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

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

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

至于 Facade 模式,我已经见过太多次了,当你最终得到 50 多个方法类时,我已经不喜欢它了……问题是:为什么需要它? 出于效率方面的考虑(类似于老式 EJB)吗?

Does SomeClass maintain an internal state, or is it just "assembling" various pieces of functionality? Can you rewrite it that way:

internal class SomeClass
{
    ...


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

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

In this case, you may not break SRP if SomeFunctionality and OtherFunctionality are somehow (functionally) related which is not apparent using placeholders.

And you have the added value of being able to select the dependency to use from the client, not at creation/DI time. Maybe some tests defining use cases for those methods would help clarifying the situation: If you can write a meaningful test case where both methods are called on same object, then you don't break SRP.

As for the Facade pattern, I have seen it too many times gone wild to like it, you know, when you end up with a 50+ methods class... The question is: Why do you need it? For efficiency reasons à la old-timer EJB?

命硬 2024-08-03 15:42:52

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

I usually group methods into classes if they use a shared piece of state that can be encapsulated in the class. Having dependencies that aren't used by all methods in a class can be a code smell but not a very strong one. I usually only split up methods from classes when the class gets too big, the class has too many dependencies or the methods don't have shared state.

清风不识月 2024-08-03 15:42:52

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

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

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

My question: should all dependencies of a class be consumed by all its functionality i.e. if different bits of functionality use different dependencies, it is a clue that these should probably live in separate classes?

It is a hint, indicating that your class may be a little incoherent ("doing more than just one thing"), but like you say, if you take this too far, you end up with a new class for every piece of new functionality. So you would want to introduce facade objects to pull them together again (it seems that a facade object is exactly the opposite of this particular design rule).

You have to find a good balance that works for you (and the rest of your team).

柒七 2024-08-03 15:42:52

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

华泰

Looks like overloading to me.
You're trying to do something and there's two ways to do it, one way or another. At the SomeClass level, I'd have one dependency to do the work, then have that single dependent class support the two (or more) ways to do the same thing, most likely with mutually exclusive input parameters.
In other words, I'd have the same code you have for SomeClass, but define it as SomeWork instead, and not include any other unrelated code.

HTH

故事和酒 2024-08-03 15:42:52

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

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

问题在于这个阶层是否会继续成长以及朝什么方向成长。 两种方法不会产生影响,但如果重复次数超过 3 个,您将需要决定是否要将其声明为外观/适配器,或者需要为变体创建子类。

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

A Facade is used when you want to hide complexity (like an interface to a legacy system) or you want to consolidate functionality while being backwards compatible from an interface perspective.

The key in your case is why you have the two different methods in the same class. Is the intent to have a class which groups together similar types of behavior even if it is implemented through unrelated code, as in aggregation. Or, are you attempting to support the same behavior but have alternative implementations depending on the specifics, which would be a hint for a inheritance/overloading type of solution.

The problem will be whether this class will continue to grow and in what direction. Two methods won't make a difference but if this repeats with more than 3, you will need to decide whether you want to declare it as a facade/adapter or that you need to create child classes for the variations.

Your suspicions are correct but the smell is just the wisp of smoke from a burning ember. You need to keep an eye on it in case it flares up and then you need to make a decision as how you want to quench the fire before it burns out of control.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文