Двухфазная инициализация
Иногда, по каким-то причинам мы не можем выполнить всю инициализацию при создании класса (в конструкторе или в
Например, мы хотим создать гейтвей для работы с БД. Следующий код не будет работать:
Проблемы многофазной инициализации:
• Объект может быть инициализирован частично, что приведет к ошибкам выполнения
• Линтеры будут требовать дополнительных проверок в методах
• Корректная последовательность инициализации класса неочевидна из его API. Ситуация становится сложнее, если у нас есть несколько вариантов второй фазы
• Возможно нарушение принципа единственности ответственности: объект смешивает логику, ради которой он создавался, и сложную процедуру инициализации
В общем случае, желательно, чтобы объект был работоспособен сразу после создания без необходимости вызова дополнительных методов. То есть, чтобы невозможно было создать объект в нерабочем состоянии.
В качестве альтернатив многофазной инициализации всегда стоит рассматривать введение дополнительной функции, классметода или даже применения паттерна абстрактная фабрика.
Дополнительные материалы
• http://neo.dmcs.p.lodz.pl/symos/wyklady/04-TwoPhase.pdf
• https://wiki.wxpython.org/TwoStageCreation
• https://peps.python.org/pep-0489/
Иногда, по каким-то причинам мы не можем выполнить всю инициализацию при создании класса (в конструкторе или в
__init__
). Например, это может быть выполнение асинхронного ввода/вывода, простановка циклических ссылок между двумя созданными объектами или особоый механизм обработки ошибок инициализации. В этом случае иногда создают вспомогательный метод, который нужно вызвать сразу после создания объекта. Стоит использовать такой подход с осторожностью.Например, мы хотим создать гейтвей для работы с БД. Следующий код не будет работать:
class SomeGW:Мы не можем выполнять async код в ините класса, поэтому можно попытаться сделать двухфазную инициализацию:
def __init__(self, db_uri):
self.connection = await asyncpg.connect(db_uri)
gw = SomeGw("postgresql://postgres@localhost/test")
class SomeGW:В таком случае необходимо следить, что соединение не будет использовано до завершения второй фазы инициализации (вызова connect). Также, у созданного объекта формально
def __init__(self):
self.connection = None
async def connect(self, db_uri):
self.connection = await asyncpg.connect(db_uri)
gw = SomeGw()
await gw.connect("postgresql://postgres@localhost/test")
self.connection
может быть None
, что приведет к дополнительным проверкам в коде всех методов и предупреждениям линтера. Проще было ввести дополнительную функцию:
class SomeGW:
def __init__(self, connection):
self.connection = connection
async def new_some_gw(db_uri):
connection = await asyncpg.connect(db_uri)
return SomeGw(connection)
gw = await new_some_gw("postgresql://postgres@localhost/test")
Проблемы многофазной инициализации:
• Объект может быть инициализирован частично, что приведет к ошибкам выполнения
• Линтеры будут требовать дополнительных проверок в методах
• Корректная последовательность инициализации класса неочевидна из его API. Ситуация становится сложнее, если у нас есть несколько вариантов второй фазы
• Возможно нарушение принципа единственности ответственности: объект смешивает логику, ради которой он создавался, и сложную процедуру инициализации
В общем случае, желательно, чтобы объект был работоспособен сразу после создания без необходимости вызова дополнительных методов. То есть, чтобы невозможно было создать объект в нерабочем состоянии.
В качестве альтернатив многофазной инициализации всегда стоит рассматривать введение дополнительной функции, классметода или даже применения паттерна абстрактная фабрика.
Дополнительные материалы
• http://neo.dmcs.p.lodz.pl/symos/wyklady/04-TwoPhase.pdf
• https://wiki.wxpython.org/TwoStageCreation
• https://peps.python.org/pep-0489/