为什么全球状态不好?

时间:2013-10-06 13:37:09

标签: ruby global-state

前几天,我在#ruby-lang频道与某人讨论@@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

然而,索蒙说,在这里使用全球国家被认为是不好的做法。

我理解为什么全局状态对依赖多个后端的东西不利,因为全局状态的全局部分停止如此全局,并且本地化到那个后端。或者它会干扰依赖注入。

但是,我真的想不出为什么这是坏事的任何其他原因。而且,如果并发性成为一个问题(需要多个后端),那么我们可以更新代码以使用Redis(或类似的东西)。

另外,我找到this question on programmers.sxc,但它无法理解为什么上面的代码被认为是如此糟糕?还有什么可以替代?

1 个答案:

答案 0 :(得分:2)

为什么不好?

由于您没有提及的几个原因,全球州很糟糕:

  1. 不可靠:因为程序中的任何内容(包括第三方代码)都可以更改变量,所以在放入变量后,您再也不能依赖于某些内容。
  2. 打破封装:如果有一个全局用户列表,程序的其他部分应该通过User类来访问它。否则,每个人都在直接操纵数据,这是一个坏主意。
  3. 难以改变:如果您发现您的全局状态需要是数组而不是哈希,那就是运气不好。您必须更改使用它的代码的每个部分,因为没有要更改的访问器。
  4. 这个更抽象,但仍然:功能纯度:当你引入全局状态时,许多以前纯函数变得不纯。这通常很糟糕,因为像C这样的编译语言可以大量优化纯函数,而在Ruby中则很糟糕,因为它会使方法更难测试。
  5. 您提到的全局状态的特定形式@@变量,对于Ruby特定原因也是不好的:

    • 它具有奇怪的继承语义:Ruby中的@@变量在类及其所有子类之间共享。所以即使你认为它是封装的,它也不是。如果有人将您的User类子类化,并声明变量@@list来存储不相关的数据,那么这尤其糟糕。 Ruby不会抱怨,并且整个User类树的状态都会受到损害。

    还能做些什么?

    • 依赖注入:只需将数据传递给任何需要它的人,而无需全局维护。
    • 类封装:如果您需要全局状态,请让类使用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