什么是最简单,最像Ruby的方法让一个构造函数返回相应类型的对象?
更具体地说,这是一个虚拟的例子:说我有两个类Bike
和Car
,它们是Vehicle
的子类。我想要这个:
Vehicle.new('mountain bike') # returns Bike.new('mountain bike')
Vehicle.new('ferrari') # returns Car.new('ferrari')
我在下面提出了一个解决方案,但它使用了allocate
,这似乎太过于实现了。有什么其他方法,或者我的确实可以吗?
答案 0 :(得分: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
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_all和https://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>