Ruby中的工厂方法

时间:2009-10-04 04:17:35

标签: ruby factory

什么是最简单,最像Ruby的方法让一个构造函数返回相应类型的对象?

更具体地说,这是一个虚拟的例子:说我有两个类BikeCar,它们是Vehicle的子类。我想要这个:

Vehicle.new('mountain bike')  # returns Bike.new('mountain bike')
Vehicle.new('ferrari')        # returns Car.new('ferrari')

我在下面提出了一个解决方案,但它使用了allocate,这似乎太过于实现了。有什么其他方法,或者我的确实可以吗?

6 个答案:

答案 0 :(得分:20)

如果我制作一个名为 1 newinitialize的工厂方法,我想这并没有真正回答“我如何制作一个。 ..构造函数......“,但我认为我会这样做......

class Vehicle
  def Vehicle.factory vt
    { :Bike => Bike, :Car => Car }[vt].new
  end
end

class Bike < Vehicle
end

class Car < Vehicle
end

c = Vehicle.factory :Car
c.class.factory :Bike

1。调用方法 factory 在这个教学示例中非常有效但是IRL你可能想在评论中考虑@AlexChaffee的建议。

答案 1 :(得分:17)

我今天这样做了。翻译成车辆看起来像这样:

class Vehicle
  VEHICLES = {}

  def self.register_vehicle name
    VEHICLES[name] = self
  end

  def self.vehicle_from_name name
    VEHICLES[name].new
  end
end

class Bike < Vehicle
  register_vehicle 'mountain bike'
end

class Car < Vehicle
  register_vehicle 'ferrari'
end

我喜欢类的标签与类本身一起保存,而不是有关于存储在超类中的子类的信息。构造函数不是new,但我认为使用该特定名称没有任何好处,这会使事情变得棘手。

> Vehicle.vehicle_from_name 'ferrari'
=> #<Car:0x7f5780840448>
> Vehicle.vehicle_from_name 'mountain bike'
=> #<Bike:0x7f5780839198>

注意某些需要确保在运行vehicle_from_name之前加载这些子类(可能这三个类将在不同的源文件中),否则超类将无法知道存在哪些子类,即你在运行构造函数时不能依赖autoload来提取这些类。

我通过将所有子类放入例如:一个vehicles子目录,并将其添加到vehicle.rb的末尾:

require 'require_all'
require_rel 'vehicles'

使用require_all宝石(在https://rubygems.org/gems/require_allhttps://github.com/jarmo/require_all找到)

答案 2 :(得分:6)

改编自here,我有

class Vehicle
  def self.new(model_name)
    if model_name == 'mountain bike'  # etc.
      object = Bike.allocate
    else
      object = Car.allocate
    end
    object.send :initialize, model_name
    object
  end
end

class Bike < Vehicle
  def initialize(model_name)
  end
end

class Car < Vehicle
  def initialize(model_name)
  end
end

答案 3 :(得分:4)

包含的模块而不是超类?这样,您仍然可以#kind_of?工作,并且没有违规行为new

module Vehicle
  def self.new(name)
    when 'mountain bike'
      Bike.new(name)
    when 'Ferrari'
      Car.new(name)
    ...
    end
  end
end

class Bike
  include Vehicle
end

class Car
  include Vehicle
end

答案 4 :(得分:2)

class VehicleFactory
  def new() 
    if (wife_allows?)
       return Motorcycle.new
    else
       return Bicycle.new
    end
  end
end

class vehicleUser 
  def doSomething(factory)
    a_vehicle = factory.new()
  end
end

现在我们可以做...

client.doSomething(Factory.new)
client.doSomething(Bicycle)    
client.doSomething(Motorcycle)

您可以在 Ruby中的设计模式Amazon link)一书中看到此示例。

答案 5 :(得分:1)

通过将Vehicle#new更改为:

,您可以稍微清理一下
class Vehicle
  def self.new(model_name = nil)
    klass = case model_name
      when 'mountain bike' then Bike
      # and so on
      else                      Car
    end
    klass == self ? super() : klass.new(model_name)
  end
end

class Bike < Vehicle
  def self.new(model_name)
    puts "New Bike: #{model_name}"
    super
  end
end

class Car < Vehicle
  def self.new(model_name)
    puts "New Car: #{model_name || 'unknown'}"
    super
  end
end

带有三元语句的Vehicle.new的最后一行非常重要。如果没有检查klass == self,我们会陷入无限循环并生成其他人之前指出的StackError。请注意,我们必须使用括号调用super。否则,我们最终会使用super不期望的参数调用它。

以下是结果:

> Vehicle.new
New Car: unknown # from puts
# => #<Car:0x0000010106a480>

> Vehicle.new('mountain bike')
New Bike: mountain bike # from puts
# => #<Bike:0x00000101064300>

> Vehicle.new('ferrari')
New Car: ferrari # from puts
# => #<Car:0x00000101060688>