Ruby中的工厂方法

Pet*_*ter 18 ruby factory

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

更具体地说,这是一个虚拟的例子:说我有两个类BikeCar哪个子类Vehicle.我要这个:

Vehicle.new('mountain bike')  # returns Bike.new('mountain bike')
Vehicle.new('ferrari')        # returns Car.new('ferrari')
Run Code Online (Sandbox Code Playgroud)

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

Dig*_*oss 20

如果我制作一个未被称为1 的工厂方法,new或者initialize我认为这并没有真正回答"我如何制作一个......构造函数......"的问题,但我认为这就是我要做的...

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

class Bike < Vehicle
end

class Car < Vehicle
end
Run Code Online (Sandbox Code Playgroud)
c = Vehicle.factory :Car
c.class.factory :Bike
Run Code Online (Sandbox Code Playgroud)

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

  • 我建议不要称它为"工厂".这使得模式与实现混淆.而是将其命名为"create"或"from_style". (8认同)

cla*_*cke 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
Run Code Online (Sandbox Code Playgroud)

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

> Vehicle.vehicle_from_name 'ferrari'
=> #<Car:0x7f5780840448>
> Vehicle.vehicle_from_name 'mountain bike'
=> #<Bike:0x7f5780839198>
Run Code Online (Sandbox Code Playgroud)

请注意,某些东西需要确保在运行vehicle_from_name之前加载这些子类(可能这三个类将位于不同的源文件中),否则超类将无法知道哪些子类存在,即您不能依赖自动加载来拉取这些子类运行构造函数时的类.

我通过将所有子类放在例如vehicles子目录中并将其添加到以下结尾来解决这个问题vehicle.rb:

require 'require_all'
require_rel 'vehicles'
Run Code Online (Sandbox Code Playgroud)

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

  • @Surya,常数是常数.一旦初始化,"车辆"不应该改变. (2认同)
  • @clacke在这种情况下,类变量是完全合适的.变化的常数完全违反了最不惊讶的原则. (2认同)

Pet*_*ter 6

改编自这里,我有

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
Run Code Online (Sandbox Code Playgroud)