Насколько я понял, инъекция зависимостей отделяет логику проводки приложения от бизнес-логики. Кроме того, я стараюсь придерживаться закона Деметры, просто вводя непосредственных соавторов.Как бороться с ошибками и исключениями во время инъекции зависимостей
Если я правильно понимаю this article, правильная инъекция зависимостей означает, что коллабораторы должны быть полностью инициализированы при их введении, если не требуется ленивое создание экземпляра. Это означало бы (и на самом деле упоминается в статье), что объекты, такие как соединения с базой данных и потоки файлов, должны быть готовы и готовы во время впрыска.
Однако открытие файлов и соединений может привести к исключению, которое должно быть обработано в какой-то момент. Каков наилучший способ сделать это?
я мог обработать исключение в «время проволоки», как показано в следующем фрагменте:
class Injector:
def inject_MainHelper(self, args):
return MainHelper(self.inject_Original(args))
def inject_Original(self, args):
return open(args[1], 'rb')
class MainHelper:
def __init__(self, original):
self.original = original
def run(self):
# Do stuff with the stream
if __name__ == '__main__':
injector = Injector()
try:
helper = injector.inject_MainHelper(sys.argv)
except Exception:
print "FAILED!"
else:
helper.run()
Это решение, однако, начинает смешивать бизнес-логику с проводкой логикой.
Другим решением является использование поставщика:
class FileProvider:
def __init__(self, filename, load_func, mode):
self._load = load_func
self._filename = filename
self._mode = mode
def get(self):
return self._load(self._filename, self._mode)
class Injector:
def inject_MainHelper(self, args):
return MainHelper(self.inject_Original(args))
def inject_Original(self, args):
return FileProvider(args[1], open, 'rb')
class MainHelper:
def __init__(self, provider):
self._provider = provider
def run(self):
try:
original = self._provider.get()
except Exception:
print "FAILED!"
finally:
# Do stuff with the stream
if __name__ == '__main__':
injector = Injector()
helper = injector.inject_MainHelper(sys.argv)
helper.run()
Недостатком здесь является дополнительная сложность поставщика и нарушение закона Деметры.
Каков наилучший способ справиться с исключениями, подобными этому при использовании рамки для инъекций зависимостей, как описано в статье?
РЕШЕНИЕ, на основе обсуждения с DJNA
Во-первых, в качестве DJNA правильно указывает, что нет никакого фактического Смешение бизнеса и проводки логики в моем первом растворе. Проводка происходит в своем собственном, отдельном классе, изолированном от другой логики.
Во-вторых, есть область применения. Вместо одного из них имеются две меньшие области:
- Область действия, в которой файл еще не подтвержден. Здесь механизм впрыска не может ничего допустить о состоянии файла и не может создавать объекты, зависящие от него.
- Область, в которой файл успешно открыт и проверен. Здесь двигатель впрыска может создавать объекты на основе извлеченного содержимого файла, не опасаясь взорвать ошибки файла.
После ввода первой области и получения достаточной информации об открытии и проверке файла бизнес-логика пытается фактически проверить и открыть файл (сбор фруктов, как полагает дджна). Здесь исключения могут быть обработаны соответствующим образом. Когда будет уверен, что файл загружен и проанализирован правильно, приложение может ввести вторую область.
В-третьих, на самом деле это не связано с основной проблемой, но все еще проблема: первое решение включает бизнес-логику в основном цикле вместо MainHelper. Это затрудняет тестирование.
class FileProvider:
def __init__(self, filename, load_func):
self._load = load_func
self._filename = filename
def load(self, mode):
return self._load(self._filename, mode)
class Injector:
def inject_MainHelper(self, args):
return MainHelper(self.inject_Original(args))
def inject_Original(self, args):
return FileProvider(args[1], open)
def inject_StreamEditor(self, stream):
return StreamEditor(stream)
class MainHelper:
def __init__(self, provider):
self._provider = provider
def run(self):
# In first scope
try:
original = self._provider.load('rb')
except Exception:
print "FAILED!"
return
# Entering second scope
editor = Injector().inject_StreamEditor(original)
editor.do_work()
if __name__ == '__main__':
injector = Injector()
helper = injector.inject_MainHelper(sys.argv)
helper.run()
Обратите внимание, что я отрезал некоторые углы в последнем фрагменте. Обратитесь к указанной статье за дополнительной информацией о вводе областей.
Я вижу, как в пуле соединений будет подходящим в этой ситуации, но это оставляет ситуацию, когда на самом деле требуется «соединение». Например, при загрузке и анализе файла конфигурации в начале среды выполнения приложений. – ghostonline
В случае, если вы должны выполнять настоящую работу как часть инъекции, то может применяться одно и то же правило - если вы не можете выполнить инъекцию, то приложение не запускается. Итак, снова есть четкое разделение обязанностей. Таким образом, два варианта: не запускать или требовать, чтобы код приложения был устойчивым к текущим сбоям. Я думаю, что чистые неудачи инициализации, о которых вы описываете, должны привести к неудаче запуска. – djna
И каков был бы лучший способ сделать это? Мое первое решение обрабатывает такие исключения «вне» всей другой бизнес-логики, но мне кажется, что это смешение проводки и бизнес-логики. – ghostonline