2

前几天我在#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

然而,somone 说这里使用全局状态被认为是不好的做法。

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

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

另外,我在programmers.sxc 上发现了这个问题,但这并不能帮助我理解为什么上面的代码被认为如此糟糕?另外,还有什么替代方案?

4

1 回答 1

4

为什么不好?

由于您没有提到的几个原因,全局状态很糟糕:

  1. 它是不可靠的:因为你的程序中的任何东西,包括第三方代码,都可以改变变量,所以你永远不能依赖在你把它放进去一秒钟后就在那里的东西。
  2. 它打破了封装:如果有一个全局用户列表,程序的其他部分应该必须通过 User 类来访问它。否则,每个人都在直接操作数据,这是一个坏主意。
  3. 很难改变:如果你发现你的全局状态需要是一个数组而不是一个散列,那么运气不好。您必须更改使用它的代码的每一部分,因为没有要更改的访问器。
  4. 这个有点抽象,但仍然:Function Purity:当你引入全局状态时,许多以前纯函数变得不纯。这通常很糟糕,因为像 C 这样的编译语言可以大量优化纯函数,而在 Ruby 中则很糟糕,因为它使方法更难测试。

您提到的全局状态的特定形式,@@变量,对于 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
    
于 2013-10-06T15:01:05.147 回答