为什么在模块中使用实例变量而不是局部变量?

Why is an instance variable used instead of a local variable in a module?

我在 Rails 应用程序上看到 Ruby 的代码:

module SessionsHelper
  def current_user
    @current_user ||= User.find_by id: session[:user_id]
  end

  ...
end

为什么在current_user方法中使用实例变量而不是局部变量?当我将其更改为局部变量时,一切都与使用实例变量相同。

我读到实例变量可以用于包含此模块的许多其他 类。但在本例中,它们定义在current_user 方法中,用于存储用户的值@current_user。这在许多其他 类 中是如何使用的?

@current_user ||= User.find_by id: session[:user_id]

这里使用了一些 Ruby 快捷方式,所以让我们扩展为:

 if @current_user == nil
   @current_user = User.find_by id: session[:user_id]
 end

 return @current_user

好的,很好。但是这个数据库查找实际上可能需要很长时间(比如我们每次调用它需要几十毫秒。)。这可能不会感觉很长时间,但它最终会加起来 - Rails 应用程序经常调用当前用户(仅请求一次与数十次也减少了数据库服务器上的负载)。 (没有任何类型的缓存 ActiveRecord 和 Rails 在你背后做的是......

所以我们将结果保存到一个实例变量中以备后用。

因此,如果您改为执行类似的操作,请在 current_user 方法中

if my_current_user
  my_current_user = User.find_by id: session[:user_id]
end

return my_current_user

这将始终进入那个 if 语句,始终进行查找,将答案放在本地并 return 它。下次它不知道它已经查找了它,所以将再次执行相同的数据库查询,获取结果等等。所以它可能会工作,但它会比在这里使用实例变量慢。

当然有缺点。 Ruby 中的模块可以导入 classes - 模块中的方法成为 class 的方法。所以是的,现在您已经为导入模块的所有 classes 创建了一个实例变量。这...可能不完全友好(如果其他一些模块也使用该名称怎么办??),但是 ehhhhhhhhhhh... 在小型初学者代码库中这无关紧要。

这大概的意思是,如果@current_user,作为全局request/response作用域(Controller/View/Helpers)的实例变量,已经被定义,就作为current_user使用。否则,为它分配与存储在会话中的 id 对应的用户做你做的事。

换句话说,这类似于:

def current_user
    if @current_user # this will be false in every hit, for the same request
        @current_user
    else
        User.find_by id: session[:user_id]
    end
end

这应该足够了。

但是,您可以在代码的其他部分再次使用 current_user,比如在控制器中,用于相同的请求。

这意味着,current_user 将不得不再次访问数据库(缓存放在一边)。如果可以的话,你想避免这种情况。那么,为什么不将用户存储在全局实例变量中呢?

因此:

def current_user
    if @current_user # this will be false for the first hit, true for every subsequent request
        @current_user 
    else
        @current_user = User.find_by id: session[:user_id]
    end
end

既然我们这样做了,为什么不使用 shorthand 方法呢?

所以,最后:

def current_user
    @current_user ||= User.find_by id: session[:user_id]
end

答案是,在相同的 request/response.

中,您这样做是为了提高可重用性