【问题标题】:Should you avoid static classes? [closed]你应该避免静态类吗? [关闭]
【发布时间】:2010-11-21 20:29:06
【问题描述】:

静态类被认为是不好的做法吗?几天前我读了一篇关于这个的文章(找不到它,抱歉),它基本上说拥有静态类(尤其是那些“帮助”类)通常是错误代码的标志。这是否正确,如果正确,原因是什么?

【问题讨论】:

  • 你的意思是“静态方法”吗?下面的示例(例如 Math.round)是静态方法的示例,但 Math 类本身不是静态的。
  • 不,我的意思是静态类。我在 Silky 的帖子中添加了评论以进行澄清。
  • 如果我们确切地知道它在说什么,以及它是否涉及特定的上下文、语言或设计理念,我们肯定会更容易反驳/同意这篇文章。
  • 什么是静态类,它适用于哪些语言?

标签: static-classes


【解决方案1】:

我认为一般的论点是反对在静态类中保持可变状态,并且基本上将它们用作全局变量。静态类中的静态辅助方法没有任何问题。

至于为什么全局变量不好,我敢肯定,在这里和谷歌之间,你会找到数百万页和博客文章。

【讨论】:

  • 是的,这是他声称的,但这并不是他实际展示的。静态类(如 Math)不等同于全局变量,它们等同于全局 constants。即使有参数,这仍然与全局常量数组、列表等在逻辑上没有什么不同。我不记得它们有什么问题。
  • 我编辑了答案以表明我的意思是静态类中的可变状态,而不是常量。
【解决方案2】:

滥用静态类可以被认为是不好的做法。但任何语言功能的滥用也是如此。

我没有区分只有静态方法的非静态类和静态类。它们实际上是相同的,除了静态类允许编译器强制执行开发人员意图(不实例化此类,访问其功能的方便语法,etc)。

正如您所说,“Helper”类的激增可能会给您带来麻烦(设计、可维护性、可读性、可发现性、其他能力......)。这里没有争论。但是你能说“Helper”类从不合适吗?我怀疑。

确实,负责任地使用静态类可以为您的代码带来很多好处:

  • Enumerable 静态类提供了一组我们大多数人都喜欢的扩展方法。它们是一组逻辑功能/业务逻辑,与任何特定类型的实例无关。
  • 环境/上下文提供的服务:例如日志记录、配置(有时)
  • 其他(我暂时想不到:))

所以不,一般来说这是不错的做法。明智地使用它们...

【讨论】:

  • 一个区别(与上述不同)是能够更好地适应随着时间的推移而发生的变化。通过使用对象,您可以使用两种不同的设置来初始化同一个类,从而实现更好的封装。
【解决方案3】:

这是否意味着“使用静态类是错误的”(在这种情况下文章不正确)或“静态类经常出现在编写糟糕的代码中”(在这种情况下,并不是说静态类本身就是不好的,但它们有时会被错误地使用)

静态函数比非静态函数更高效,因为您无需创建对象实例即可使用它们或将“this”指针传递给方法调用。

使用静态类来保存全局变量是另一回事 - 在这种情况下,基本规则不是“静态不好”,而是“全局不好”。

【讨论】:

    【解决方案4】:

    我在我的代码中一直使用静态实用程序类来处理那些经常被调用但很难实例化的方法。一个示例是一个简单的日志记录类,例如:

    public static class Logging
    {
      public static UpdateAction(int id, object data)
      {
         SqlConnection connection = new SqlConnection("conn string from config file");
         // more logic here...
      }
    }
    

    现在,我从来没有让这些类和方法存储任何类型的全局状态,因为这会导致巨大的并发问题,而且通常只是糟糕的设计。

    所以不要避免使用静态类,只是避免让这些静态类保持某种全局状态。

    【讨论】:

    • 如何对静态方法进行单元测试?如果重构它们,如何确保没有引入回归?
    【解决方案5】:

    这里有一些 google 测试博客文章的链接,这些文章介绍了为什么静态方法会损害代码的可测试性,以及为什么静态方法(这是静态类的坏部分)会引入各种可能令人惊讶的耦合和交互组件之间。

    how to think about oo

    singletons

    static-methods-are-death-to-testability

    【讨论】:

    • 所有这些文章都是同一个人写的。原来他只是一个咆哮的Ruby偏执狂。他的整个论点用他自己的话来概括:“静态方法的基本问题是它们是过程代码。我不知道如何对过程代码进行单元测试。”哇。 50 年的测试方法早于这个人,他对此一无所知。我能想象的唯一比公开承认这一点更令人尴尬的是,如果我们其他人的设计和测试方法基于一个显然在大学里睡过头的人的意见。
    • 他是个白痴。他声称,在您编写了像 Abs() 这样的静态方法之后,您将决定在其中添加许多您无法测试的额外业务逻辑。测试逻辑单元的重点不是在它内部完成了多少工作,而是它为所有可能的输入提供了适当的输出。如果您添加更多代码(静态或非静态),您只需要添加更多单元测试。
    • Rbarry:我会说他更像是一个 OO 偏执狂而不是 Ruby 偏执狂,他的帖子标记为咆哮:)
    • 他有一个由三部分组成的系列,人们似乎将他的不经意的不知情的意见视为对测试和设计未来的前沿洞察力。 (见证关于它们的多个问题)。我说 Ruby-bigot 是因为他坚持认为 [Math.]Abs(X) 是错误的,并且 X.Abs() 的“Ruby 做对了”。 (我认为我们应该两者兼得……)
    • 我发现 Misko Hevery 关于如何考虑可测试性的信息是试图回答此处原始问题的最清晰的信息。他谈论接缝的地方是有助于在非静态方面犯错的地方。我的推荐是这个视频misko.hevery.com/2008/11/04/clean-code-talks-unit-testing我的感觉是,你消化他的文章越多,论点就越有凝聚力。尽管如此,请注意不要被卷入黑与白——这里是正确和错误的观点——那里只有激烈的宗教辩论。
    【解决方案6】:

    您需要一个实用程序类,其中所有方法都是静态的。在这种情况下,如果您将类设为静态,则它清楚地表明了您的意图。至少在 C# 中,静态类中不能有非静态方法。

    【讨论】:

      【解决方案7】:

      不,这并不完全正确。

      有很多函数不是特定于对象实例的,并且不需要状态。例如考虑“数学”函数。

      几乎所有语言都有类似的内容:

      y = Math.round(x);
      

      所以'round'函数是静态的。如果你疯了,你可以为 (C#) 之类的东西辩护:

      class RoundFunction<T> : GeneralMathFunction<T>
      {
          public override T Operate (params object[] variables) {
              ..
          }
      }
      

      但是,恕我直言,这样做会有点奇怪。

      当然,有时静态函数过多是出现问题的迹象,但在合理的范围内,它并不“坏”。

      【讨论】:

      • Silky,正如 Barry Brown 所指出的,这些是静态方法。我只是指完全静态的。 :)
      • 如果是c#,静态类只是意味着它只包含静态方法。您只是在声明没有理由制作它的实例。所以同样的规则适用,就像我的帖子一样。
      • 如果我们实际上是在尝试面向对象,round() 不应该是浮点方法吗?
      • 彼得:嗯,你也可以四舍五入双数/小数。
      • 顺便说一句,在 .NET 中,Math 完全是静态类。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-25
      • 2013-09-30
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 2014-10-24
      • 1970-01-01
      相关资源
      最近更新 更多