【问题标题】:Separate Class vs Method单独的类与方法
【发布时间】:2010-09-27 19:27:58
【问题描述】:

快速设计问题。

ClassA 有一个名为 DoSomething(args) 的方法

在DoSomething()中,在它真正能做某事之前,它需要对args做一些准备工作。我认为这应该封装在 ClassA 中(而不是在外面做准备工作并将其传入),因为没有其他东西需要知道 DoSomething 需要这个准备工作。

然而,让我思考的是实际的准备工作代码所属的地方。

在我的特定示例中,准备工作是从 args 创建满足特定条件的项目列表。

我的预感是我应该创建一个新类 ListOfStuff,它在其构造函数中接受 args 并将这个准备工作放在这里。

从 TDD 的角度来看,我认为这是正确的选择。然后我们可以对 ListOfStuff 进行单元测试,直到我们心满意足。如果我们把准备工作放在 ClassA 的私有方法中,我们只能通过测试 DoSomething() 来间接测试它。

但这是矫枉过正吗?自从采用 TDD 和 DI 方法以来,我已经看到我编写的类的数量成倍增加 - 我应该担心吗?

Ta。

【问题讨论】:

  • 对于单元测试,使用 InternalsVisibleTo 属性。这是一个非常有用的属性。它允许“朋友”程序集查看另一个程序集的内部方法/属性/成员。
  • 示例:使用系统;使用 System.Runtime.CompilerServices; [assembly:InternalsVisibleTo("ByBox.BusinessLogic.UnitTests")] 命名空间 ByBox.BusinessLogic { ....

标签: unit-testing language-agnostic tdd mocking


【解决方案1】:

最简单的方法是什么?这就是 TDD 的口头禅。不要试图想得太远。如果需要创建帮助类,您就会知道,因为您将在其他类的多个方法中完成各种相关工作。在那之前,按照你的方法完成工作。如果它使方法太长或难以阅读,请将工作提取到自己的方法中。这种方法也可以在不需要其他类的情况下进行测试。

【讨论】:

  • 我以为你只测试了公共方法?单元测试私有方法,即使是通过单元测试框架暴露的黑客攻击,对我来说总是有点“不确定”。
  • 这取决于您的问题域。我公司的产品是一个类库 - 我们真的希望对私有或内部方法进行单元测试。
  • @Duncan - 对于单元测试,使用 InternalsVisibleTo 属性。这是一个非常有用的属性。它允许“朋友”程序集查看另一个程序集的内部方法/属性/成员。
  • 我没有说你必须测试这个方法,如果它是私有的。我说你可以测试一下。就个人而言,我不会,因为我不直接依赖它并且测试它会使我的测试变得脆弱。过早地把它移到另一个班级,我认为是一个错误。
  • 嗯好的。这引发了另一个问题 - 是否测试私有方法 - 但我确信那里有很多我可以赶上的材料。我的直觉总是不测试私有方法,因为它们是间接测试的。否则你会花费大量不切实际的时间来编写测试。
【解决方案2】:

您的对象模型设计应该单独考虑,而不是在您的开发策略的上下文中。如果构建 ListOFStuff 并传递给 DoSomething 确实是对象模型最适合组合在一起的方式,那么请执行此操作,无论您的开发策略如何

我想你已经回答了一些你自己的问题,但是,由于 ListOfStuff 使单元测试更容易,这可能意味着它也是一个更简洁的设计

希望有帮助!

【讨论】:

  • 我不得不说 ListOfStuff 将纯粹作为一个助手创建,因此不会出现在任何对象模型设计中。嗯。但我确实喜欢它,因为它是用于测试的显式接口。嗯。可能是那些“没有正确或错误答案”的案例之一!
【解决方案3】:

一定要把它放在一个新的类中。它称为关注点分离。你不想重载一个类并让它做各种各样的其他事情。这是因为您的课程将无法在其他任何地方使用,因为它只针对一件事。

把它放在类中,然后在其他地方使用那个类。否则你以后就得一次又一次地写这个。

为了使其具有可扩展性,并能够传入各种不同的算法,您所追求的设计模式是Strategy 模式。但那是为了未来……

【讨论】:

  • 一般来说,如果代码也用于其他地方,我同意。这不是这里的情况。这里只用在这一种方法中。创建一个新方法,我可以看到,但是在这一点上创建一个新类将是大材小用。
  • @tvanfosson - 你是对的,目前,该代码不会在其他地方使用。我熟悉“如果多次使用则重构”的口号——但我现在也看到设计更多地是关注点分离。但我“担心”(!)我太过分了!
  • @Duncan,但与计算有关的唯一方法是当前方法。你需要平衡原则——在这种情况下,SoC 需要与 YAGNI 平衡(你不需要它)。
【解决方案4】:

这里有几个启发式方法。

  1. 在这个类上是否有状态表明 从调用到 调用?这项准备工作是否得到 每次你需要做 doSomething(),或者它完成了吗? 得救了吗?如果是这样,那就证明了 类。
  2. 是否需要进行此计算 在不止一次的地方?如果是这样的话, 争论一个班级。
  3. 可以详细的 doSomething() 的实现 方法,或准备工作 它,改变而不影响 封闭类?如果是这样,那争论 上课。

嗯,三个启发式。没有人会想到西班牙宗教裁判所。

【讨论】:

  • 绝对不适合 1 和 2,也许 适合 3。我确实喜欢它将所有这些准备工作封装在一个整洁的课程中的方式,但我想我可能会在这里把关注点分离的事情看得太极端了。这是一个周五下午的想法!
  • 不,在我看来你的想法是对的。你可能有一个私有内部类的论据;否则将准备工作放在私有方法中。
【解决方案5】:

自从采用 TDD 和 DI 方法,我已经看到了 我写的类乘以 - 应该 我担心吗?

如果您的目标是过程编程,是的。但既然您几乎肯定希望以 OO 方式工作,那么不。

大多数人使用的类型太少,花在 OO 设计上的时间太少。

您关于班级责任的问题反映了思维的成熟(恕我直言)。

【讨论】:

    【解决方案6】:

    仅当所讨论的“关注点”与课程无关,或者您发现课程共享此“关注点”(又名,违反 DRY)时,SoC 才有效。在您的情况下,该代码似乎是该类所固有的 - 所以private 函数可能更合适。

    正如 tvanfosson 上面所说,你需要平衡 SoC 和 YAGNI。就个人而言 - 我认为您可能过早地考虑这个问题(我知道!我也一直这样做)。

    【讨论】:

    • 是的,我认为如果您将 SoC 作为纯粹的学术练习,或者 ListOfStuff 做了一些非常复杂的事情并且我想自己测试它,那么我会有一个 ListOfStuff 类可以做到这一点工作。但是,从务实的角度来看,我只是将所有工作都放在 ClassA 中。妥协!
    猜你喜欢
    • 2021-11-19
    • 2012-12-25
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多