Фабричный паттерн
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 43 из 57.
Паттерн фабрика помещает создание объектов в одно место. Вызывающий код запрашивает то, что ему нужно, часто по имени, и получает готовый объект, не зная, экземпляр какого класса был создан:
class Square
def initialize(side)
@side = side
end
def area
@side * @side
end
end
class Circle
def initialize(r)
@r = r
end
def area
(3.14159 * @r * @r).round(2)
end
end
class ShapeFactory
def self.create(kind, size)
case kind
when "square" then Square.new(size)
when "circle" then Circle.new(size)
end
end
end
puts ShapeFactory.create("square", 3).area
puts ShapeFactory.create("hexagon", 3).inspectРезультат:
9
nilХеш, сопоставляющий имена с классами, делает фабрику короче и упрощает её расширение. Классы являются объектами, поэтому их можно хранить в хеше и отправлять им new:
class Dog
def speak
"Woof"
end
end
class Cat
def speak
"Meow"
end
end
class AnimalFactory
KINDS = { "dog" => Dog, "cat" => Cat }.freeze
def self.create(kind)
klass = KINDS.fetch(kind) { raise ArgumentError, "unknown animal: #{kind}" }
klass.new
end
end
puts AnimalFactory.create("cat").speak
begin
AnimalFactory.create("cow")
rescue ArgumentError => e
puts e.message
endРезультат:
Meow
unknown animal: cowФабрика также может позволить другому коду регистрировать новые виды во время выполнения, поэтому добавление вида не означает редактирование фабрики:
class Plugins
@kinds = {}
class << self
def register(name, klass)
@kinds[name] = klass
end
def build(name)
@kinds.fetch(name).new
end
def names
@kinds.keys.sort
end
end
end
class Zip
def run
"zipping"
end
end
Plugins.register("zip", Zip)
puts Plugins.build("zip").run
p Plugins.namesВывод:
zipping
["zip"]Вызывающий код зависит только от того, что могут делать объекты — здесь area или speak, — и никогда не зависит от конкретных классов. Новая фигура означает один новый класс и одну новую запись в фабрике, без изменений где-либо ещё:
shape = ShapeFactory.create(kind, size) # Square, Circle, ...
puts shape.area # вызывающий код никогда не называет классЗадание
ЛегкоЗавершите реализацию фигур и ShapeFactory.create(kind, size). Square, Circle и Triangle (равносторонний треугольник) используют общий базовый класс Shape, метод to_s которого возвращает square 3: area 9.00, указывая имя класса в нижнем регистре, размер и площадь с двумя знаками после запятой. Используйте 3.14159 для числа пи и Math.sqrt(3) / 4 * size**2 для треугольника. Фабрика ищет вид в замороженной константе-хэше KINDS и возвращает nil для неизвестного вида.
Предоставленный код считывает строки square 3, запрашивает у фабрики каждую фигуру и выводит её либо unknown shape: hexagon.
Ваш код должен находиться в файлах shape.rb, square.rb, circle.rb, triangle.rb и shape_factory.rb. В main.rb находится предоставленный код ввода-вывода, и его нельзя изменять.
Попробуйте сами
require_relative 'shape'
require_relative 'square'
require_relative 'circle'
require_relative 'triangle'
require_relative 'shape_factory'
# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
kind, size = line.split
shape = ShapeFactory.create(kind, size.to_f)
puts shape || "unknown shape: #{kind}"
end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПеременные экземпляраМетоды и selfto_s и inspectПовторение — читательский билет4Инкапсуляция
Приватные методыЗащищённые методыПриватные сеттерыЗащита состоянияПовторение — торговый автомат7Полиморфизм и утиная типизация
ПолиморфизмУтиная типизацияАбстрактные методыcase и классыПовторение — способы оплаты10Шаблоны проектирования, часть 2
Делегирование с помощью ForwardableШаблон декоратораШаблон строителяШаблон состояния2Аксессоры и self
Чтение и записьПользовательские сеттерыself в методахИдентичность и равенствоПовторение — список контактов5Наследование
ПодклассыПереопределение методовВызов superis_a? и предкиПовторение — расчёт зарплаты сотрудников8Специальные методы
Равенство и хешированиеМетоды операторовИндексация и добавлениеПользовательские коллекцииПовторение — дроби11Проект: Управление библиотекой
Книги и читателиВыдача книг3Методы и переменные классов
Методы классовПеременные экземпляра классаКонстантыФабричные методыПовторение — коды купонов6Модули и примеси
Модули и includeРасширение объектовМодуль ComparableМодуль EnumerableПовторение — спортивная лига9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор Ruby