【问题标题】:Sharing a class instance between two classes在两个类之间共享一个类实例
【发布时间】:2014-05-21 19:26:43
【问题描述】:

我有两个不同的类,它们都代表需要持久化到我的数据库的对象,现在我想在这两个类之间共享数据库客户端对象。我想避免多次实例化客户端对象。

目前我通过使用全局变量来做到这一点

$client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")

class Person
  def save
    $client.query("INSERT INTO persons")
  end
end

class Car
  def save
    $client.query("INSERT INTO cars")
  end
end

这可行,但我想知道是否有更正确的方法可以做到这一点以及为什么它们更正确?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    您可以从父类继承。这允许您跨对象共享通用功能并遵循 DRY(不要重复自己)编程原则。它还允许您使用锁、恢复、队列、池以及您可能想做的任何其他事情来保护您的数据库连接,而不必担心在您的孩子课程中这样做

    class Record
      @table_name = nil
      @@client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
    
      def save
        @@client.query("INSERT INTO #{@table_name}") if @table_name
      end
    end
    
    class Person < Record
      @table_name = "persons"
    end
    
    class Car < Record
      @table_name = "cars"
    end
    

    当我们讨论这个主题时,您应该考虑使用 ActiveRecord 来处理您的数据库模型和连接。它已经完成了您需要的几乎所有工作,并且与现有的其他 gem 更加兼容。 It can be used without rails.

    【讨论】:

    • 我提出问题的原因之一是我不想多次实例化数据库客户端对象。在使用您的解决方案时,@@client 是一个类变量这一事实是否意味着每个子类共享同一个对象?
    • @SimonThordal A class variable is shared by all instances of a class source.
    • 这个答案有正确的想法,但它可以使用一些重构。 @@client 可以设置在 initialize 之外(以避免 if)。 attr_accessor 的使用似乎没有必要,因为 1) 它只能从类内部调用,2) 对于同一类的实例,table_name 应该始终相同。
    【解决方案2】:

    作为使用继承的替代方法,为什么不考虑一个简单的Singleton pattern?通过将责任分离到类之外,这可以使您的模型更清晰。并消除了继承的需要。

    下面的例子说明了这一点。 DataManager 类只能存在一个实例。因此,您只需将其实例化一次 - 但可以在任何地方使用它:

    require 'singleton'
    
    class DataManager
      include Singleton
    
      attr_accessor :last_run_query
    
      def initialize()
        if @client.nil?
          p "Initialize the Mysql client here - note that this'll only be called once..."
        end
      end
    
      def query(args)
        # do your magic here
        @last_run_query = args
      end
    
    end
    

    接下来,使用 .instance 访问器调用它是一件轻而易举的事 - 并且总是指向一个实例,如下所示:

    # Fetch, or create a new singleton instance
    first = DataManager.instance
    first.query('drop table mother')
    p first.last_run_query
    
    # Again, fetch or create a new instance
    # this'll actually just fetch the first instance from above
    second = DataManager.instance
    p second.last_run_query
    
    # last line prints: "drop table mother"
    

    作为记录,单例模式可能有一些缺点,并且经常使用它会导致a never-ending debate 关于你是否应该使用它。但在我看来,对于您的具体问题,这是一个不错的选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多