关于代码设计:你应该避免静态类吗?

Should you avoid static classes?

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


滥用静态类被认为是不好的做法。但滥用任何语言的特征也是如此。

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

正如您所说,大量的"助手"类会给您带来麻烦(设计、可维护性、可读性、可发现性、其他能力…)。这里没有争论。但是你能说"助手"类永远不合适吗?我对此表示怀疑。

实际上,负责任地使用静态类对您的代码有很大的好处:

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

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


它是指"静态类的使用是错误的"(在这种情况下,文章是错误的)还是"静态类通常在写得不好的代码中被发现"(在这种情况下,它不是说静态类本身是错误的,而是说它们有时被错误地使用)

静态函数比非静态函数更有效,因为您不需要创建对象的实例来使用它们,也不需要将"this"指针传递给方法调用。

使用静态类来保存全局变量是另一回事——在这种情况下,底层规则不是"静态是坏的",而是"全局是坏的"。


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

至于为什么全局变量是坏的,我敢肯定,在这里和谷歌之间,你会找到50万页的网页和博客文章。


不,这不太正确。

有许多函数不是特定于对象实例的,不需要状态。例如,考虑"数学"函数。

几乎所有语言都有这样的特点:

1
y = Math.round(x);

所以"round"函数是静态的。如果你疯了,你可以为(c)这样的事情辩护:

1
2
3
4
5
6
class RoundFunction<T> : GeneralMathFunction<T>
{
    public override T Operate (params object[] variables) {
        ..
    }
}

但是,天哪,你这样做有点奇怪。

当然,有一段时间,过多的静态函数是出了问题的征兆,但在合理的范围内,这并不是"坏的"。


下面是一些链接,指向一些谷歌测试博客文章,说明为什么静态方法会损害代码的可测试性,以及为什么静态方法(静态类中不好的部分)会引入各种可能令人惊讶的组件之间的耦合和交互。

如何思考OO

独生子女

静态方法是可测试性的死


在我的代码中,我一直在使用静态实用程序类来处理那些经常调用的方法,但是要声明这些方法会很困难。一个例子是一个简单的日志类,比如:

1
2
3
4
5
6
7
8
public static class Logging
{
  public static UpdateAction(int id, object data)
  {
     SqlConnection connection = new SqlConnection("conn string from config file");
     // more logic here...
  }
}

现在,我绝不会让这些类和方法存储任何类型的全局状态,因为这会导致巨大的一致性问题,一般来说,这只是一个糟糕的设计。

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


在实用程序类中,所有方法都是静态的,这是有必要的。在这种情况下,如果您使类静态化,它将清楚地指示您的意图。至少在C中,静态类中不能有非静态方法。