【问题标题】:Why is an instance variable used instead of a local variable in a module?为什么在模块中使用实例变量而不是局部变量?
【发布时间】:2017-12-26 15:56:36
【问题描述】:

我在 Ruby on Rails 应用程序中看到了这段代码:

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

  ...
end

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

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

【问题讨论】:

    标签: ruby memoization


    【解决方案1】:

    @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 语句,始终进行查找,将答案放入本地并返回。下次它不知道它已经查过了,所以将再次执行相同的数据库查询,获取结果等。所以它可能会工作,但它会比在这里使用实例变量慢。

    当然也有缺点。 Ruby 中的模块可以导入到类中——模块中的方法成为类的方法。所以是的,现在您已经为导入您的模块的所有类创建了一个实例变量。这……可能不完全是善良的(如果其他模块也使用该名称怎么办??),但是 ehhhhhhhhhhh....在一个小的初学者代码库中它并不重要。

    【讨论】:

      【解决方案2】:

      这大致意思是,如果@current_user作为全局请求/响应范围(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
      

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

      所以,最后:

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

      答案是,您这样做是为了在同一个请求/响应中进行可重用性调整。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多