为什么全局变量不好?

最近有考虑这个问题,百度了下,感觉不是很满意,看到有点赞多的答案,就搬运过来。原文链接:https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil。 既然是译文,我就尽量保持原文的信息,但是,翻译水平有限,如果有错误,还望不吝赐教。


Q:为什么全局变量是魔鬼?

在讨论这个问题之前,我先说清楚,我对概念—抽象、依赖注入很了解。我不需要这方面的科普。

我们经常说“不要使用全局变量”“单例(singletons)是魔鬼,因为他们使用全局变量”,却没有真正的理解,全局变量真的有这么不吉利吗?

我们经常需要给应用设置全局配置,比如系统文件夹路径、或者面向所有应用的(application-wide)数据库的证书。

这种情况下,除了把这些设置放在一个所有应用都能访问的全局空间,我想不到更好的方法了。

我知道,滥用全局变量是不好的,但是,全局变量真的有那么邪恶吗?如果真是这样,有什么好的替代方案吗?


A: 总结成一句话,全局变量让程序的状态变得不可预测。
(译者注:状态指,局部变量。全局状态,指全局变量。)
具体的说,想象一下,你有使用同一个全局变量的两个对象。假设你在两个模块中都没有修改全局变量(这里有点难翻译,详细见原文)。那么,任意指定方法(指成员函数)的输出是可以预测的(也是可以测试的),如果你知道在执行这个方法之前系统的状态。

但是,如果有一个对象的一个方法,改变了全局变量的状态,它便触发了全局变量的副作用。这种情况下,你就不知道,另一个对象执行一个方法时的初始状态。当你执行方法,你不再能够预测你能得到的输出,因此,也无法测试。

在学术层面似乎没有那么严重,但是,能够单元测试代码是证明代码正确性(或者,至少满足目标)过程中的重要一步。

在现实中,这种情况可能产生非常严重的后果。假设,你有一个类产生一个全局变量结构,另一个类消费在这个结构中的数据,在程序中改变它的状态或者销毁它。
如果消费类在生产类还没有生产的时候消费,结果是,消费类可能处理不完整的数据,生产类的数据结构可能毁坏或销毁。在这种情况下,程序的行为变得完全不可预测,可能会造成很大的损失。

而且,全局变量有损代码的可读性。如果你的代码有一个外部依赖,它没有显式的引入代码中。那么,无论谁来维护你的代码,都必须要查找,找到它的出处。

至于,是否有替代品存在。是不可能完全没有全局变量。但是,实践中,把所有全局变量包装成一个对象,是有可能。并且,这个变量不能被,你使用的语言的作用域规则引用。如果一个指定的对象需要一个指定的状态,它就应该通过给构造函数传入参数或者通过setter函数,显式的请求这个状态。这就是 依赖注入(Dependency Injection)。

你明明可以通过你使用的语言的作用域规则直接访问,却要通过参数传入状态,看起来是有点傻的,但是,优点是巨大的。如果,有人直接看这个代码片,状态和它的出处是很清楚的。从代码模块的灵活性、它可能被用于其他上下文的角度来说,好处也是巨大的。如果状态是传入的、只在局部代码块中改变它的状态,那么,你可以传入任意的值(只要它的数据类型是正确的)。然后,让你的代码处理。用这种方法写的代码,看起来,组件之间的耦合度很低,修改起来会比较简单。模块中的代码不许要关心,这个状态只从哪里来,只需要关心如何处理它。如果你将状态(state)传入一个代码块,这个代码块便可以独立,不再和依赖全局状态(global state)一样。

有很多其他的原因,为什么传入状态比依赖全局状态好得多。这个答案并不想穷举所有原因。你可能可以写出整整一本书,为什么全局变量是坏的。


其他说法:因为大家都可以改变,所以,你不能依赖它。


A2 :看到深入理解操作系统里有另一个不用全局变量的理由。函数名和已初始化的全局变量称为强符号(strong symbol),未初始化的全局变量称为弱符号(weak symbol)。如果有多个同名弱符号,链接器会任意(是的,任意,random)选择一个,即使这些弱符号的类型可能不一样。这样操作全局变量,会导致内存访问问题(详细请参考:深入理解操作系统第三版)。

阅读更多
换一批

没有更多推荐了,返回首页