常数是否像全局变量和单身一样邪恶?

You*_*nse 4 php singleton dependency-injection constants global-variables

我在这个论坛上多次听说使用全局变量是一种死罪,实施单身人士是一种犯罪.

我突然想到,古老的好常数具有这些耻辱行为的所有特征:它们可以全球访问,毫无疑问它们引入了全球最先进的状态.

所以,问题是:我们不应该将常规声明声明为常量,并且一直使用所有现代的东西,如DI,IoC或其他时髦的单词吗?

Spu*_*ley 7

全局变量被认为是不良实践的主要原因是因为它们可以在系统的一个部分中进行修改并在另一个部分中使用,而在这两个代码之间没有直接的联系.

这会导致潜在的错误,因为可以编写使用全局变量的代码,而不知道(或考虑)使用它的所有位置以及可以更改它的方式.反之亦然,编写对全局进行更改的代码,而不会意识到更改可能会对代码的其他无关部分产生影响.

常数不会分享这个问题,因为它们是......好的,不变的.一旦定义它们,它们就不能被改变,因此上段中描述的发布不会发生.

因此,它们可以在全球范围内使用.

也就是说,我已经看到一些define用于创建常量的编写糟糕的PHP代码,但在不同情况下以不同方式声明常量.这是对常量的误用:常量应该是绝对固定的值; 它应该只是一个单一的价值.如果在程序的不同运行中有某些东西可能是不同的值,那么它不应该被定义为常量.那种事情确实应该是一个变量,然后应该遵循与其他变量相同的规则.

这种误用只能在像PHP这样的脚本语言中发生; 它不可能在编译语言中发生,因为您只能在一个地方和固定值中定义一次常量.


Gor*_*don 7

一般来说是的,避免常数.它们引入了消费者与全球范围的耦合.也就是说,消费者依赖外面的东西.这是不明显的,例如

class Foo
{
    public function doSomething()
    {
        if (ENV === ENV_DEV) {
            // do something this way
        } else {
            // do something that way
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在不知道内部结构的情况下doSomething,您不会知道具有该常量的全局范围存在依赖性.因此,除了使您的代码更难以理解之外,您还限制了如何重用它.

对于只有一个值的常数,例如,上述情况也是如此

public function log($message)
{
    fwrite(LOGFILE, $message);
}
Run Code Online (Sandbox Code Playgroud)

这里的常量指向外部某处定义的文件资源

define('LOGFILE', fopen('/path/to/logfile'));
Run Code Online (Sandbox Code Playgroud)

这和使用一样不明显ENV.它是一种依赖,需要在类之外存在某些东西.我必须知道,为了使用该对象.由于使用此常量的类隐藏了这个细节,我可能会尝试在不确定常量存在的情况下记录某些内容,然后我会想知道为什么它不起作用.它甚至不必是一个资源,LOGFILE可以简单地将路径包含为字符串.结果相同.

依赖于消费者中的全局常量,还需要在单元测试中设置全局状态.这是你通常想要避免的,即使常量是固定值,因为单元测试的要点是单独测试单元并且必须将环境置于某种状态会妨碍这一点.

而且,使用全局常量总是会造成不同库冲突的常数的威胁.根据经验,不要将任何东西放入全局范围.如果必须使用命名空间,则使用命名空间来聚类常量.

但是,请注意,命名空间常量仍然存在关于耦合的相同问题,因此类常量也是如此.只要这个耦合在同一个命名空间内就不那么重要了,但是一旦你开始从各个命名空间耦合到常量,你就会再次妨碍重用.就此而言,考虑任何常量公共API.

使用常量的另一种方法是使用不可变的值对象,例如:

class Environment
{
    private $value;

    public function __construct($value)
    {
        $this->assertValueIsAllowedValue($value);
        $this->value = $value;
    }

    public function getValue() {
// …
Run Code Online (Sandbox Code Playgroud)

这样,除了确保值有效之外,您还可以将这些值传递给需要它们的对象.像往常一样,YMMV.这只是一个选择.单个常量不会使您的代码无法使用,但主要依赖于常量将产生不利影响,因此根据经验,尝试将它们保持在最低限度.

在相关的旁注中,您可能还对以下内容感兴趣:


Pet*_*eld 5

global variable和 global constant之间有很大的区别。

避免使用全局变量的主要原因是它可以随时被任何东西修改。它可以在调用/执行顺序上引入各种隐藏的依赖关系,并且有时会导致相同的代码工作而不是其他代码,这取决于全局是否以及如何更改。显然,如果您正在处理并发性或并行性,那么糟糕的魔力可能会更加严重。

全局常量在整个代码中始终(或应该)完全相同。一旦您的代码开始执行,就可以保证查看它的每一段代码每次都会看到相同的内容。这意味着没有引入意外依赖项的危险。常量的使用实际上非常有助于提高可靠性,因为这意味着如果您需要更改代码,则无需在多个位置更新值。(永远不要低估人为错误!)

单身是另一个问题。这是一种经常被滥用的设计模式,它基本上最终会成为全局变量的面向对象版本。在某些语言(例如 C++)中,如果您不注意初始化顺序,它也可能会出错。然而,它有时可能是一种有用的模式,尽管通常有更好的替代方案(尽管有时需要稍微多一些工作)。

编辑:只是简单地扩展一下,您在问题中提到全局常量引入了“有史以来最全局的状态”。这并不准确,因为全局常量是(或应该)以与源代码相同的方式修复的。它定义了程序的静态特性,而“状态”通常被理解为动态运行时概念(即可以改变的东西)。