Фабрика — это просто способ создания объектов. В отличие от шаблона Builder, который создает объект по частям, factory создает его оптом.

Есть три способа сделать это -

  1. Заводской метод
  2. Фабрика
  3. Абстрактная фабрика

Заводской метод

class TypePoint(Enum):
  CARTESIAN,
  POLAR

class Point:
  def __init__(self, x, y, type_point):
    if type_point == TypePoint.CARTESIAN:
        self.x = x
        self.y = y
    elif type_point == TypePoint.POLAR:
        self.x = x * cos(y)
        self.y = x * sin(y)

Если вводится какой-то новый тип точки, нам нужно изменить метод __init__. Это нарушает принцип открытия-закрытия.

Идея состоит в том, чтобы в этом случае создавать статические методы всякий раз, когда вводится новый тип точки. Фабричный метод — это не что иное, как статический метод.

class Point:
  def __init__(self, x, y):
    self.x = x
    self.y = y
  
  @staticmethod
  def type_point_cartesian(x, y):
    return Point(x, y)
  
  @staticmethod
  def type_point_polar(x, y):
    return Point(x * cos(y), x * sin(y))

# How to access
p1 = type_point_cartesian(1,2)
p2 = Point(2,3) #No way to avoid this since no concept of private initializers.
p3 = type_point_polar(4,5)

Фабрика

Давайте применим принцип единой ответственности к приведенному выше примеру, и он станет фабрикой. Фабрику можно рассматривать как класс с фабричными методами. Эти фабричные методы не обязательно должны быть статическими, так как теперь они находятся в другом классе. Фабрика также может быть внутренним классом, что имеет место в других языках программирования, но поскольку __init__ является общедоступным в python, на самом деле не имеет значения, является ли фабрика внутренней по отношению к классу Point или вне его.

Давайте посмотрим на пример, где factory является внутренним классом.

class Point:
  def __init__(self, x = 0, y = 0):
    self.x = x
    self.y = y
  
  class PointFactory:
    def type_point_cartesian(self, x, y):
      return Point(x, y)
    
    def type_point_polar(self, x, y):
      return Point(x * cos(y), x * sin(y))
    
  factory = PointFactory() # This partially makes factory Singleton
# How to access
p1 = Point()
p1.factory.type_point_polar(2, 3)

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

Здесь особо нечего объяснять. Просто подключите абстрактный класс, и он станет абстрактной фабрикой.

class HotDrinkFactory: #Abstract Factory
  def prepare():
    pass

class TeaFactory(HotDrinkFactory):
  def prepare():
    return Tea()

class CoffeeFactory(HotDrinkFactory):
  def prepare():
    return Coffee()

def make_drink(type):
  if type == 'Tea':
    return TeaFactory.prepare()
  elif type == 'Coffee':
    return CoffeeFactory.prepare()
  else:
    return None

#Just call the make_drink method with the required type

Ссылка –

Курс Udemy: https://www.udemy.com/course/design-patterns-python/