Фабричный метод

ПРоблема
Логика создания объекта может становиться слишком запутанной и конструктор становится не информативным

Примеры:

- ряд обязательных и необязательных параметров, параметров со значениями по умолчанию
- параметр невозможно перегрузить одним и тем же набором аргументов с разными именами

Для решения таких задач можно передать тонкости создания объекта отдельному методы(фабричный метод) или отдельному классу (Фабрика). Также можно использовать иерархию Фабрик используя Абстрактную Фабрику
Типы шаблонов проектирования
В шаблоне Фабрика мы создаем объекты, не раскрывая логику создания клиенту, и клиент использует общий интерфейс для создания нового типа объекта.

Идея состоит в том, чтобы использовать статическую функцию, которая создает и возвращает экземпляры, скрывая от пользователя детали модулей класса.

Фабричный метод — это один из основных принципов проектирования для создания объекта, позволяющий клиентам создавать объекты библиотеки, чтобы они были не тесно связаны с иерархией классов библиотеки.

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

1) Указать координаты в Декартовой системе координат
2) Указать координаты в Полярной системе координат

Для этого опишем два Фабричных метода, которые позволяют при создании объекта использовать разную логику
Фабричные методы создания класса Point

from dataclasses import dataclass
from math import cos, sin


@dataclass
class Point:
    x: float
    y: float

    def __str__(self):
        return f'x: {self.x}, y: {self.y}'

    @staticmethod
    def new_cartesian_point(x, y):
        return Point(x, y)

    @staticmethod
    def new_polar_point(rho, theta):
        return Point(rho * sin(theta), rho * cos(theta))


if __name__ == '__main__':
    p1 = Point.new_cartesian_point(2, 3)
    p2 = Point.new_polar_point(3, 6)
    print(p1)
    print(p2)