![Фабричний метод](/images/patterns/cards/factory-method-mini.png?id=72619e9527893374b98a5913779ac167)
Фабричний метод на Ruby
Фабричний метод — це породжуючий патерн проектування, який вирішує проблему створення різних продуктів, без прив’язки коду до конкретних класів продуктів.
Фабричний метод задає метод, який необхідно використовувати замість виклику оператора new
для створення об’єктів-продуктів. Підкласи можуть перевизначити цей метод, щоб змінювати тип створюваних продуктів.
Якщо ви вже чули про Фабрику, Фабричний метод чи Абстрактну фабрику, але вам все одно важко їх розрізняти, то прочитайте нашу статтю Порівняння фабрик.
Складність:
Популярність:
Застосування: Патерн можна часто зустріти в будь-якому Ruby-коді, коли потрібна гнучкість при створенні продуктів.
Ознаки застосування патерна: Фабричний метод можна визначити за створюючими методами, які повертають об’єкти продуктів через абстрактні типи або інтерфейси. Це дозволяє змінювати типи створюваних продуктів у підкласах.
Концептуальний приклад
Цей приклад показує структуру патерна Фабричний метод, а саме — з яких класів він складається, які ролі ці класи виконують і як вони взаємодіють один з одним.
main.rb: Приклад структури патерна
# The Creator class declares the factory method that is supposed to return an
# object of a Product class. The Creator's subclasses usually provide the
# implementation of this method.
class Creator
# Note that the Creator may also provide some default implementation of the
# factory method.
def factory_method
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
# Also note that, despite its name, the Creator's primary responsibility is
# not creating products. Usually, it contains some core business logic that
# relies on Product objects, returned by the factory method. Subclasses can
# indirectly change that business logic by overriding the factory method and
# returning a different type of product from it.
def some_operation
# Call the factory method to create a Product object.
product = factory_method
# Now, use the product.
"Creator: The same creator's code has just worked with #{product.operation}"
end
end
# Concrete Creators override the factory method in order to change the resulting
# product's type.
class ConcreteCreator1 < Creator
# Note that the signature of the method still uses the abstract product type,
# even though the concrete product is actually returned from the method. This
# way the Creator can stay independent of concrete product classes.
def factory_method
ConcreteProduct1.new
end
end
class ConcreteCreator2 < Creator
# @return [ConcreteProduct2]
def factory_method
ConcreteProduct2.new
end
end
# The Product interface declares the operations that all concrete products must
# implement.
class Product
# return [String]
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete Products provide various implementations of the Product interface.
class ConcreteProduct1 < Product
# @return [String]
def operation
'{Result of the ConcreteProduct1}'
end
end
class ConcreteProduct2 < Product
# @return [String]
def operation
'{Result of the ConcreteProduct2}'
end
end
# The client code works with an instance of a concrete creator, albeit through
# its base interface. As long as the client keeps working with the creator via
# the base interface, you can pass it any creator's subclass.
def client_code(creator)
print "Client: I'm not aware of the creator's class, but it still works.\n"\
"#{creator.some_operation}"
end
puts 'App: Launched with the ConcreteCreator1.'
client_code(ConcreteCreator1.new)
puts "\n\n"
puts 'App: Launched with the ConcreteCreator2.'
client_code(ConcreteCreator2.new)
output.txt: Результат виконання
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}
App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}