2015-08-17 5 views
2

Я хочу использовать хэш для создания объектов определенного типа, однако вскоре возникают проблемы, потому что хеш будет только создавать экземпляр одного экземпляра, а затем навсегда «указывать» на этот экземпляр.Использование хэша в качестве фабрики в Ruby

class Person 
end 

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

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

a == b 
=> true 

Даже если хэш указывает на метод, скажем, например, make_person, который вызывает внутри него Person.new, он все равно будет всегда указывать на один экземпляр.

У кого-нибудь есть обходное решение для этого? Большое спасибо.

ответ

4

Объект (экземпляр Person) присваивается клавише «make_person».

Каждый раз, когда вы запрашиваете ключ "make_person", вы возвращаете этот объект.

Вам необходимо сделать инициализацию Person ленивым, используя lambda.

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

factory["make_person"].call 
+0

Использование лямбда работает как шарм. Благодаря! – efreezy

0

Вы ссылающийся на тот же экземпляр 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 

Зададим его переменной а:

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

Зададим его переменной Ь :

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

Теперь одновременно & б указывают на тот же объект, как показано на 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> 
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 для чего-то нет никаких причин, чтобы использовать хэш, вы можете сделать это:

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 
0

Нет идея, почему вы хотите, чтобы он был хешем. Но если вы настаиваете, мы здесь:

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 
Смежные вопросы