Я сделал это сегодня. В переводе на транспортные средства это будет выглядеть так:
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 (предположительно, эти три класса будут в разных исходных файлах), иначе суперкласс не сможет узнать, какие существуют подклассы, т. е. вы не можете зависеть от автозагрузки, в которую можно загружать эти классы при запуске конструктора.
Я решил это, поместив все подклассы, например, в. подкаталог vehicles
и добавление его в конец vehicle.rb
:
require 'require_all'
require_rel 'vehicles'
Использует камень require_all
(находится в https://rubygems.org/gems/require_all и https://github.com/jarmo/require_all)