2010-09-14 6 views
1

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

Если я правильно понимаю 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() 

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

ответ

0

У меня была дискуссия об этом в контексте Java EE, EJB 3 и ресурсов.

Я понимаю, что нам нужно различать инъекцию ссылки на ресурс и фактическое использование ресурса.

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

InjectedConnectionPool icp; 

public void doWork(Stuff someData) throws Exception{ 

     Connection c = icp.getConnection(). 
     c.writeToDb(someData); 
     c.close(); // return to pool 


} 

Как я понимаю:

1). То, что внедренный ресурс не может быть самим соединением, скорее, он должен быть пулом соединений. Мы берем соединения на короткое время и возвращаем их. 2). То, что любое соединение Db может быть аннулировано в любое время по ошибке в БД или сети. Таким образом, ресурс объединения пулов должен иметь возможность избавиться от плохих соединений и получить новые. 3). Неисправность впрыска означает, что компонент не будет запущен. Это может произойти, если, например, инъекция на самом деле является поиском JNDI. Если нет записи JNDI, мы не можем найти определение пула соединений, не можем создать пул и поэтому не можем запустить компонент. Это не то же самое, что фактически открытие соединения с БД ... 4). ... во время инициализации нам фактически не нужно открывать какие-либо соединения, неспособность сделать это просто дает нам пустой пул - то есть. точно такое же состояние, как если бы мы работали некоторое время, и БД ушла, пул мог/мог/удалять устаревшие соединения.

Эта модель, по-видимому, прекрасно определяет набор обязанностей, которые может принять Деметра. Инъекция имеет возможность подготовить почву, убедитесь, что когда код должен что-то сделать, он может. Код несет ответственность за сбор плодов, попытку использовать подготовленный материал и справиться с фактическими сбоями ресурсов, а также против сбоев в поиске около ресурсов.

+0

Я вижу, как в пуле соединений будет подходящим в этой ситуации, но это оставляет ситуацию, когда на самом деле требуется «соединение». Например, при загрузке и анализе файла конфигурации в начале среды выполнения приложений. – ghostonline

+0

В случае, если вы должны выполнять настоящую работу как часть инъекции, то может применяться одно и то же правило - если вы не можете выполнить инъекцию, то приложение не запускается. Итак, снова есть четкое разделение обязанностей. Таким образом, два варианта: не запускать или требовать, чтобы код приложения был устойчивым к текущим сбоям. Я думаю, что чистые неудачи инициализации, о которых вы описываете, должны привести к неудаче запуска. – djna

+0

И каков был бы лучший способ сделать это? Мое первое решение обрабатывает такие исключения «вне» всей другой бизнес-логики, но мне кажется, что это смешение проводки и бизнес-логики. – ghostonline