Обновить

Комментарии 27

Действительно полезная статья

Python-альтернатива: просто пользуйтесь модулями (серьёзно)

Если у вас в sys.path есть вложенные пути, то можно один и тот же модуль заимпортить два раза и это будут разные модули. За много лет, я только один раз с таким сталкивался.

Заступлюсь за строителя, иногда он удобен и уместен. Например в ArgumentParser.

Странно, что вы про декоратор не сказали.

Аналогично заступлюсь. Например класс QuerySet в Django уместно реализует строителя

Вроде, Java поддерживает функции первого порядка.

public int cumulativeScore(Function<String, Integer> wordScore, List<String> words) { ... }
...
Function<String, Integer> scoreFunction = w -> score(w);
...
public Function<String, String> createGreeter(String greeting) {
    return (String name) -> greeting + ", " + name + "!";
}


Ну, и Java Platform Module System как раз реализует механизм "модулей, как пространства имен".

Билдер в питоне действительно часто лишний из-за наличия keyword-аргументов функции, но на самом деле это очень удобная вещь, когда речь идёт о конструировании со сложной логикой. Query builder из ORM это буквально тот же паттерн.

Синглтон же - просто антипаттерн как и всякие мутабельные статические переменные (только хуже).

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

Хоронили GoF, порвали два баяна

Согласен с тезисом "нужно делать проще", это факт и оверинжиниринг ни у чему хорошему не приводит на дистанции. Но:

Синглтон - антипаттерн ужо много лет, строитель, в вашем примере, не то, о чем писали GoF.

Суть паттерна строитель не в классе с методами SetSomething, а в комбинации builder+director. Строитель у вас это врождённое нечто из мира ООП, которое в питоне, конечно ни к чему. Ну и логики обычно там поболе, параметрами по-умолчанию не отделаешься.

Как мне кажется в статье рассмотрены самые неверно понимаемые паттерны. Экстраполировать на все остальное я бы не стал.

В общем-то паттерны существуют для того, чтобы упрощать сложную логику. Если сложной логики в приложении нет - то и накручивать смысла нет.

Однако, стратегия, например, очень даже повсеместно используемый паттерн, без которого расширяемую реализацию писать ой как тяжело.

В C++ код живёт в заголовочных файлах и файлах исходников, и вся эта информация перемешивается в ходе компиляции. Невозможно чётко выразить: «это значение является приватным в рамках этого файла»

Ну, здрасти. Для этого есть ключевое слово static или анонимное пространство имен.

За строитель тоже затуплюсь. Он именно про конструирование сложного объекта. 2 аргумента - это не сложный объект. А вот когда аргументов с добрый десяток и 3-4 перегрузки конструктора - это адский ад. На python, в виду того, что у него перегрузка достаточно недавно появилась (да и то не уверен, робит ли с конструкторами), особо больно с этим, когда в конструкторе какой-то зверский код разбора всех этих комбинаци. Другая затея - это парсинг, десериализация, восстановление состояния и что-то подобное. В этих случаях строитель присутствует либо как отдельная сущность, либо является составной частью механизма парсинга/десериализации/восстановителя. Ну, это просто удобно: скормил строителю dict, полученный из json, а на выходе получил готовый экземпляр.

На python, в виду того, что у него перегрузка достаточно недавно появилась

Нету в Питоне перегрузки. Есть синтаксис для типов, а так одна функция которая принимает все аргументы и сама с ними разбираетcя.

В C++ .... Невозможно чётко выразить: «это значение является приватным в рамках этого файла» или «этот глобальный объект существует только однажды», приходится импровизировать.

А как же "static" для объявления локальных переменных? Хоть в рамках файла, хоть в рамках функции.

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

Вместо синглтона используйте модули, ага. Уже научитесь наконец то использовать DI, через контейнеры там или как в FastApi через Depends хотя бы

При втором вызове к Singleton(name="Bob", age=25) не создаётся ничего нового — код просто тихо переиспользует оригинальный объект с его исходными атрибутами.

Это же откровенно кривая реализация. Конструктор должен быть скрыт, а синглетон "must be accessible to clients from a well-known access point".

Просто создавайте объект на уровне модуля — и он гарантированно будет синглтоном, пока этот модуль импортируется:

В этом и проблема!!!!! Если ты будешь создавать переменные во время импорта модуля это просто ужас!
Импорт это импорт, а не вызов чего-то.
Прелесть синглтона через new в том, что ты можешь создать этот объект когда ты захочешь, а не во время импорта.
У нас был один прогер, который это пихал абсолютно везде. Тесты на такой код писать удовольствие сильно ниже среднего.
В runtime импортировать идея плохая, любые автоформатеры (black, isort, pylint) тебя съедят и будут ругаться на каждый чих, если ты будешь так делать.
И писать unit на Классовые синглтоны сильно проще.

Про использование замыкания, лучше помолчу...

Про декортаро lru_cache тоже промолчим

Импорт это импорт, а не вызов чего-то.

У меня для вас полохие новости. Это exec текста в файле и кэширование результата.

Прелесть синглтона через new в том, что ты можешь создать этот объект когда ты захочешь, а не во время импорта.

Можно это сделать лениво при первом обращении.

И писать unit на Классовые синглтоны сильно проще.

А на простые классы еще проще. Я использую переменки модуля иногда, но если использую класс то этой синглтонщиной не заморачиваюсь внутри него, кэширование можно через функцию сделать (фабричный метод).

Хочу продолжения, про другие паттерны.

Есть ещё прекрасный тег: Python OOP. Но почему-то его не любят.

Паттерт "строитель" активно используется, например, в Scala, где значения по умолчанию присутствуют. Причины его использования далеки от предположения автора статьи. Это и множество параметров, необходимых для инстанциации объекта, и разные источники их получения. Так же строители позволяют в процессе выбрать, например, нужный класс для создания объекта.

Синглтоны в виде, описанном автором, всегда были ересью. Если при создании синглтона вам нужны параметры извне, вы что-то явно делаете не так. Если вам нужно наследование от синглтона, вы, опять же, что-тг не поняли в синглтонах. Возможно, вам нужен пул объектов. Или внедрение зависимостей. Вопрос о том, нужны ли синглтоны вообще, оставим за скобками.

Паттерн Строитель нужен в основном тогда, когда вы постепенно создаёте объект с действительно сложной внутренней структурой. Например, в зависимости от того, что именно передадут, инициализируете по-разному сложные структуры данных, взаимосвязи между частями и т.п., а состав объекта может получиться разным Разумеется, применять данный шаблон для того, чтобы задать поле color, которое ни на что больше не влияет -- это настолько unpythonic, насколько возможно.

Что же до нужды в синглетонах, то поведение, связанное с инициализацией, просто совершенно не нужно вшивать в класс через __new__(). А вот статический метод get_instance() вполне может иметь смысл. Правда, и это чаще всего не нужно, как вы и написали. Кстати, самый смешной и нелепый подход к реализации синглетона в Python -- это Borg. Это правда очень странно

Вообще мне кажется, что половина проблем возникает даже не из самого паттерна, а из-за неудачной попытки вшить поведение в код неуместным в большинстве случаев языковым механизмом (new, метаклассы и подобное) вместо написания чего-то элементарного. Именно языковой механизм даёт интересные побочки типа странного поведения при наследовании.

А почему банда четврех из двух паттернов? Это "абстрактный метод"? 🤣

Ваш материал интересный, но, на мой взгляд, в трактовке паттерна Builder есть недопонимания. Как использовать паттерн решает лишь разработчик, но Вы упускаете первоначальный смысл паттерна.

1. Цепной вызов методов

Вы утверждаете, что Builder всегда предполагает цепочку (.setX().setY().build()). Это лишь синтаксический приём, уместный в Java/C++. В Python Builder работает так же корректно и без неё:

builder = SystemBuilder()

builder.set_address("127.0.0.1")

builder.set_guid("deadbeef")

system = builder.build()

2. Builder ≠ значения по умолчанию

В статье Builder фактически подменяется конструктором с параметрами по умолчанию.

Но назначение у него другое — пошаговое построение и валидация, когда объект не существует, пока он не полностью собран.

3. Опасность инициализации через None

Вы предполагаете возможность такого подхода:

class Car:

    def __init__(self, color=None, engine=None):

        self.color = color

        self.engine = engine

Но это небезопасно:

car = Car()

car.drive()   # объект есть, но engine = None → падение

Builder решает проблему иначе — поля задаются пошагово, а объект появляется только после финального build(), где возможна централизованная проверка:

builder = CarBuilder()

builder.set_engine("V8")

builder.set_color("red")

car = builder.build()  # гарантированно корректный объект

Итог: Builder в Python — не про умолчания и не про цепочки, а про создание объектов без риска получить «пустой» или неконсистентный экземпляр.

К тому же в коде

class Car:
    def __init__(self, color: str = "black", engine: str = "V4"):
        self.color = color
        self.engine = engine

car = Car(color="red", engine="V8")

конструкцию с геттером и сеттером надо было оставить.

Статья крайне любопытна как иллюстративный пример того, что из изначально ложного тезиса можно сделать абсолютно любой вывод.

«Вот, смотрите, молоток: молотком попадают по пальцам, а то и по голове поэтому давайте откажемся от молотка, а для всех «молотковых» задач будем использовать клей!», — такого примерно порядка рассуждение. Батенька, так дураку стеклянный половой орган дай, там согласно поговорке все далее будет печально.

Синглтон с параметрами, ну вы серьезно? Наследование от синглтона? Просто гениальный пример — ровно в стиле анекдота про «таракан без ног не слышит».

Пример с билдером — остается только молча орать, даже как-то комментировать неловко.

Отличная статья, спасибо огромное!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
piter.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия