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

时间:2013-11-05 11:39:09

标签: php dependency-injection singleton global-variables constants

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

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

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

3 个答案:

答案 0 :(得分:7)

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

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

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

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

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

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

答案 1 :(得分:7)

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

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

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

对于只有一个值的常量,例如

,情况也是如此
public function log($message)
{
    fwrite(LOGFILE, $message);
}

此处常量指向外部定义为

的文件资源
define('LOGFILE', fopen('/path/to/logfile'));

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

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

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

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

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

class Environment
{
    private $value;

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

    public function getValue() {
// …

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

在相关的旁注中,您可能也有兴趣:

答案 2 :(得分:4)

全局变量与全局常量之间存在很大差异。

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

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

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

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