Весенняя РАСПРОДАЖА
Абстрактная фабрика

Абстрактная фабрика на Ruby

Абстрактная фабрика — это порождающий паттерн проектирования, который решает проблему создания целых семейств связанных продуктов, без указания конкретных классов продуктов.

Абстрактная фабрика задаёт интерфейс создания всех доступных типов продуктов, а каждая конкретная реализация фабрики порождает продукты одной из вариаций. Клиентский код вызывает методы фабрики для получения продуктов, вместо самостоятельного создания с помощью оператора new. При этом фабрика сама следит за тем, чтобы создать продукт нужной вариации.

Сложность:

Популярность:

Применимость: Паттерн можно часто встретить в Ruby-коде, особенно там, где требуется создание семейств продуктов (например, внутри фреймворков).

Признаки применения паттерна: Паттерн можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.

Концептуальный пример

Этот пример показывает структуру паттерна Абстрактная фабрика, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.

main.rb: Пример структуры паттерна

# Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают
# различные абстрактные продукты. Эти продукты называются семейством и связаны
# темой или концепцией высокого уровня. Продукты одного семейства обычно могут
# взаимодействовать между собой. Семейство продуктов может иметь несколько
# вариаций, но продукты одной вариации несовместимы с продуктами другой.
#
# @abstract
class AbstractFactory
  # @abstract
  def create_product_a
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # @abstract
  def create_product_b
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика
# гарантирует совместимость полученных продуктов. Обратите внимание, что
# сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то
# время как внутри метода создается экземпляр конкретного продукта.
class ConcreteFactory1 < AbstractFactory
  def create_product_a
    ConcreteProductA1.new
  end

  def create_product_b
    ConcreteProductB1.new
  end
end

# Каждая Конкретная Фабрика имеет соответствующую вариацию продукта.
class ConcreteFactory2 < AbstractFactory
  def create_product_a
    ConcreteProductA2.new
  end

  def create_product_b
    ConcreteProductB2.new
  end
end

# Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс.
# Все вариации продукта должны реализовывать этот интерфейс.
#
# @abstract
class AbstractProductA
  # @abstract
  #
  # @return [String]
  def useful_function_a
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Конкретные продукты создаются соответствующими Конкретными Фабриками.
class ConcreteProductA1 < AbstractProductA
  def useful_function_a
    'The result of the product A1.'
  end
end

class ConcreteProductA2 < AbstractProductA
  def useful_function_a
    'The result of the product A2.'
  end
end

# Базовый интерфейс другого продукта. Все продукты могут взаимодействовать друг
# с другом, но правильное взаимодействие возможно только между продуктами одной
# и той же конкретной вариации.
#
# @abstract
class AbstractProductB
  # Продукт B способен работать самостоятельно...
  #
  # @abstract
  def useful_function_b
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # ...а также взаимодействовать с Продуктами A той же вариации.
  #
  # Абстрактная Фабрика гарантирует, что все продукты, которые она создает,
  # имеют одинаковую вариацию и, следовательно, совместимы.
  #
  # @abstract
  #
  # @param [AbstractProductA] collaborator
  def another_useful_function_b(_collaborator)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Конкретные Продукты создаются соответствующими Конкретными Фабриками.
class ConcreteProductB1 < AbstractProductB
  # @return [String]
  def useful_function_b
    'The result of the product B1.'
  end

  # Продукт B1 может корректно работать только с Продуктом A1. Тем не менее, он
  # принимает любой экземпляр Абстрактного Продукта А в качестве аргумента.
  #
  # @param [AbstractProductA] collaborator
  #
  # @return [String]
  def another_useful_function_b(collaborator)
    result = collaborator.useful_function_a
    "The result of the B1 collaborating with the (#{result})"
  end
end

class ConcreteProductB2 < AbstractProductB
  # @return [String]
  def useful_function_b
    'The result of the product B2.'
  end

  # Продукт B2 может корректно работать только с Продуктом A2. Тем не менее, он
  # принимает любой экземпляр Абстрактного Продукта А в качестве аргумента.
  #
  # @param [AbstractProductA] collaborator
  def another_useful_function_b(collaborator)
    result = collaborator.useful_function_a
    "The result of the B2 collaborating with the (#{result})"
  end
end

# Клиентский код работает с фабриками и продуктами только через абстрактные
# типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать
# любой подкласс фабрики или продукта клиентскому коду, не нарушая его.
#
# @param [AbstractFactory] factory
def client_code(factory)
  product_a = factory.create_product_a
  product_b = factory.create_product_b

  puts product_b.useful_function_b
  puts product_b.another_useful_function_b(product_a)
end

# Клиентский код может работать с любым конкретным классом фабрики.
puts 'Client: Testing client code with the first factory type:'
client_code(ConcreteFactory1.new)

puts "\n"

puts 'Client: Testing the same client code with the second factory type:'
client_code(ConcreteFactory2.new)

output.txt: Результат выполнения

Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

Абстрактная фабрика на других языках программирования

Абстрактная фабрика на C# Абстрактная фабрика на C++ Абстрактная фабрика на Go Абстрактная фабрика на Java Абстрактная фабрика на PHP Абстрактная фабрика на Python Абстрактная фабрика на Rust Абстрактная фабрика на Swift Абстрактная фабрика на TypeScript