全局常量是反模式吗?

时间:2009-08-12 02:40:39

标签: design-patterns oop anti-patterns

我一直认为只是为了保持常量而有一个类是一个糟糕的设计。但是最近,我尝试使用Google搜索,发现只有接口作为常量才是反模式 - 没有提到使用一类常量。

我认为,由于一类常量实际上与全局变量没有多大差别,这就是为什么我反对它并倾向于重构这些类。它创建了一类绝对没有上下文的数据。这些常量与实际使用它们的关系更好,给它们上下文和意义,以及将它们封装在一个类中。

其他人的想法是什么?

7 个答案:

答案 0 :(得分:27)

全局常量很好。

全球(非常数)变量是魔鬼的工作。

答案 1 :(得分:14)

全球常数不是不好的做法,只要它们是......

  1. ... immutable - 对可变对象(如Java final或C#readonly)的全局ArrayList<T> / List<T>引用不是常量,但是全球状态。
  2. ...需要&gt; 1级。如果只有一个类需要你的常量,那么将常量直接放在类中。 (警告:适当地平衡DRY和YAGNI。)
  3. Bloch涵盖了Effective Java中的“常量接口”与“常量类”问题,并提倡“常量类”方法。您不希望接口中存在常量的原因是它诱使客户端类“实现”该接口(为了访问常量而不使用接口名称作为前缀)。但是,您不应该 - 接口实际上不是对象功能的接口,而是在类外部类型中根深蒂固的编译时方便性。考虑一下:

    interface C { public static final int OMGHAX = 0x539; }
    class A implements C { ... }
    class B { private A a; }
    

    B现在不必要地依赖C。如果A的实现发生变化,因此不需要C中的常量,则无法在不破坏其外部接口的情况下从中移除implements C - 某人(可能是非常愚蠢的)人,但这样的人比比皆是)可以通过A参考引用C对象!

    通过将常量放在类中,并通过使该类不可实现,您通知客户端常量类实际上只是作为子命名空间。在C#中,您将该类标记为static,在Java中,您希望将其设为final并提供无法访问的构造函数:

    final class C { 
        private C() { throw new AssertionError("C is uninstantiable"); }
        public static final int OMGHAX = 0x539; 
    }
    

    如果您使用Java编程并希望常量不使用常量类名称作为前缀,则可以使用import static功能。

    是的,被迫创建一个新类型只是为了放置你的常量,这有点多余,但这是我们必须处理的Java和C#等语言的疣 - 我们将我们的常量放在某处,我们最好的选择恰好是一个不可实例化的类。

答案 2 :(得分:10)

全局变量存在问题,因为它们在模块之间引入了很多不必要的依赖关系。这些依赖性使得调试问题和重用代码变得更加困难。

我会说真正的全局常量也有同样的问题,所以不要让一个名为MyGlobals的单例包含像MyGlobals.HTTP_SUCCESS_OK这样的常量,而是在它们自己的类中将常量包装在一起,例如HttpStatus.SUCCESS_OK。

答案 3 :(得分:5)

我认为全局变量问题是他们创造了全球状态。全局常量不会这样做,但它们确实对一些无环境常量负责,这可能是坏事。

如果您需要这样的东西,我建议创建枚举(如果您有int常量)或常量的静态类,那么您可以给它们一些上下文(例如,Math.PI)

答案 4 :(得分:4)

我认为没有提到的一件事是更实际的问题。如果您使用的是编译语言,请记住您必须重新编译才能更改常量的值。如果它是您可能想要经常更改的值,您可能还需要考虑配置文件。

答案 5 :(得分:2)

在某些情况下,当全局常量真正保持不变时,它们是完美的方式(不仅对于程序的一个构建是恒定的,而且应该在软件产品的整个生命期内以及之后)。

例如,你不希望有几个类,每个类都声明自己的pi,e或HTTP_SUCCESS常量。

另一方面,如果全局常量是可以改变的任意值,则全局常量可以创建许多全局变量的问题。因为需求的变化。即如果将这些常量放入配置文件似乎是一个合理的选择,它不应该是一个全局常量。

答案 6 :(得分:0)

全球变量已被广泛认为是一件坏事,通常应该避免。这就是为什么很多人都遇到了Singleton模式的问题。全局变量的问题在于它们是可传递的。