Насколько я понял, внедрение зависимостей отделяет логику подключения приложения от бизнес-логики.Кроме того, я стараюсь придерживаться закона Деметры, вводя только прямых соавторов.
Если я правильно понимаю эту статью , правильное внедрение зависимостей означает, что соавторы должны быть полностью инициализированы при вводеЕсли только ленивый экземпляр не требуется.Это будет означать (и на самом деле упоминается в статье), что такие объекты, как соединения с базой данных и потоки файлов, должны быть готовы и готовы во время внедрения.
Однако открытие файлов и соединений может привести к исключению, которое должно бытьобрабатываются в какой-то момент.Каков наилучший способ сделать это?
Я мог бы обработать исключение в «время провода», как в следующем фрагменте:
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, в моем первом решении нет фактического смешения бизнес-логики и логики проводки.Проводка происходит в своем собственном, отдельном классе, изолированном от другой логики.
Во-вторых, есть случай с областями видимости.Вместо одной существуют две меньшие области:
- Область, в которой файл еще не проверен.Здесь механизм инъекций пока не может предположить что-либо о состоянии файла и не может создавать объекты, которые зависят от него.
- Область, в которой файл успешно открыт и проверен.Здесь механизм впрыска может создавать объекты на основе извлеченного содержимого файла, не беспокоясь о том, что он может взорваться из-за ошибок файла.
После ввода первой области и получения достаточной информации об открытии и проверкеfile, бизнес-логика пытается на самом деле проверить и открыть файл (сбор плодов, как выражается 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()
Обратите внимание, что я вырезал несколько углов в последнем фрагменте.Обратитесь к упомянутой статье для получения дополнительной информации о вводе областей.