前几天我在 #ruby-lang channel 上与某人讨论了 @@class_variables
。这一切都始于用户询问跟踪连接到他的服务器的用户的最佳方式是什么(我稍微简化了它,但这就是它的要点)。
所以,我建议:
class User
@@list = {} #assuming he wants to look up users by some type of ID
def initialize(user_id, ...)
@@list[user_id] = self
#...
end
end
但是,有人说在这里使用全局状态被认为是不好的做法。
我理解为什么全局状态对于依赖多个后端的东西来说是不好的,因为全局状态的全局部分不再那么全局化,并且变得本地化到该一个后端。或者它会干扰依赖注入(inject)。
不过,我真的想不出任何其他原因说明这很糟糕。而且,如果并发性成为问题(需要多个后端),那么我们可以更新代码以使用 Redis(或类似的东西)。
另外,我发现了this question on programmers.sxc ,但这并不能帮助我理解为什么上面的代码被认为如此糟糕?另外,替代方案是什么?
最佳答案
为什么不好?
由于您没有提到的几个原因,全局状态很糟糕:
- 它不可靠:因为程序中的任何内容(包括第三方代码)都可以更改变量,因此您永远不能依赖于将其放入后一秒就存在的东西。
- 它破坏了封装:如果有一个全局用户列表,程序的其他部分应该必须通过 User 类来访问它。否则,每个人都直接操纵数据,这是一个坏主意。
- 很难改变:如果你发现你的全局状态需要是一个数组而不是一个散列,那么你运气不好。您必须更改使用它的代码的每个部分,因为没有需要更改的访问器。
- 这个有点抽象,但仍然:函数纯度:当引入全局状态时,许多以前的纯函数变得不纯。一般来说,这很糟糕,因为像 C 这样的编译语言可以大量优化纯函数,而在 Ruby 中也很糟糕,因为它使方法更难测试。
您提到的全局状态的特定形式,@@
变量,对于 Ruby 特定的原因也是不好的:
- 它有奇怪的继承语义:Ruby 中的
@@
变量在类及其所有子类之间共享。因此,即使您认为它是封装的,但事实并非如此。如果有人子类化您的User
类,并声明一个变量@@list
来存储不相关的数据,这尤其糟糕。 Ruby 不会提示,整个 User 类树的状态都会受到损害。
还能做什么?
- 依赖注入(inject):只需将数据传递给需要它的人,而无需全局维护它。
类封装:如果您需要全局状态,请让类使用 setter 和 getter 来维护它。这会使第 2 点和第 3 点以及
@@
探针无效,因为它使用类@
变量。示例:class User class << self @list = {} def add_user(uid, user) #Do validation here @list[uid] = user end #More methods like add_user end def initialize(user_id, ...) User.add_user(user_id, self) end end
关于ruby - 为什么全局状态不好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19209468/