【问题标题】:Using a hash as a factory in Ruby在 Ruby 中使用哈希作为工厂
【发布时间】:2015-11-10 13:56:54
【问题描述】:

我想使用散列来实例化某种类型的对象,但是很快就会出现问题,因为散列只会实例化一个实例,然后永远“指向”该实例。

class Person
end

factory = { "make_person" => Person.new }

a = factory["make_person"]
b = factory["make_person"]

a == b
=> true

即使哈希指向一个方法,例如 make_person,它在其内部调用 Person.new,它仍将永远指向单个实例。

有人有解决方法吗?非常感谢。

【问题讨论】:

    标签: ruby methods hash factory


    【解决方案1】:

    对象(Person 的实例)正在分配给“make_person”键。

    每次您请求"make_person" 密钥时,您都会返回该对象。

    您需要使用 lambda 对 Person 进行惰性初始化。

    factory = { "make_person" => lambda { Person.new } }
    
    factory["make_person"].call
    

    【讨论】:

      【解决方案2】:

      正确的答案是使用哈希是错误的。

      为什么不使用普通的旧 Ruby 类?

      class Factory
        def self.[](type)
          case type
          when "make_person"
            Person.new
          when "make_other_thing"
            # ...
          end
        end
      end
      
      a = Factory["make_person"]
      b = Factory["make_person"]
      a == b # => false
      

      如果你想变得更漂亮:

      class Factory
        class << self
          def [](type)
            return send(type) if respond_to?(type)
            raise "I don't know how to do `#{type}'!"
          end
      
          def make_person
            Person.new
          end
      
          def make_other_thing
            # ...
          end
        end
      end
      

      ...但是你可以直接调用Factory.make_person,所以[] 方法似乎是多余的。

      如果你真的,真的想要使用 Hash 来做一些没有理由使用 Hash 的事情,你可以这样做:

      factory = {
        "make_person" => -> { Person.new }
      }
      
      a = factory["make_person"][]
      b = factory["make_person"][]
      a == b # => false
      

      或者这个:

      factory = Hash.new do |_, key|
        case key
        when "make_person"
          Person.new
        when "make_other_thing"
          # ...
        end
      }
      
      a = factory["make_person"]
      b = factory["make_person"]
      a == b # => false
      

      【讨论】:

        【解决方案3】:

        您引用的是 Person 的同一个实例,而不是 Person 的一些新实例。

        让我们挖掘:

        [3] pry(main)> factory = {}
        => {}
        [4] pry(main)> factory.object_id
        => 70344866826960
        [5] pry(main)> class Person
        [5] pry(main)* end
        => nil
        [6] pry(main)> factory["make_person"] = Person.new
        => #<Person:0x007ff4e4136038>
        

        Person被实例化了,我们来看看它的object_id:

        [7] pry(main)> factory["make_person"].object_id
        => 70344887611420
        

        让我们把它赋值给变量a:

        [8] pry(main)> a = factory["make_person"]
        => #<Person:0x007ff4e4136038>
        

        让我们将它分配给变量 b:

        [9] pry(main)> b = factory["make_person"]
        => #<Person:0x007ff4e4136038>
        

        现在 a 和 b 都指向同一个对象,如 object_id 所示

        [10] pry(main)> a.object_id
        => 70344887611420
        [11] pry(main)> b.object_id
        => 70344887611420
        

        正如 Kris 所指出的,有几种方法可以让你获得这种行为,但以上内容对你来说很重要。

        关于如何确保每次调用factory["make_person"] 时都会实例化一个新人的示例:

        factory["make_person"] = lambda { Person.new }.call
        => #<Person:0x007ff4e0fa6ba8>
        

        【讨论】:

          【解决方案4】:

          不知道你为什么希望它是一个哈希。但如果你坚持,我们就开始吧:

          factory = Hash.new do |_, key|
            blueprints = { "make_person" => Person }
            blueprints[key].new
          end
          
          person_x = factory["make_person"]
          person_y = factory["make_person"]
          
          person_x.object_id == person_y.object_id # => false
          

          【讨论】:

            猜你喜欢
            • 2011-07-01
            • 2017-03-23
            • 1970-01-01
            • 2020-07-02
            • 1970-01-01
            • 2012-01-01
            • 2018-02-19
            • 2021-02-05
            • 2020-09-06
            相关资源
            最近更新 更多