Как бороться с ошибками и исключениями во время внедрения зависимости - PullRequest
1 голос
/ 14 сентября 2010

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

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

Однако открытие файлов и соединений может привести к исключению, которое должно бытьобрабатываются в какой-то момент.Каков наилучший способ сделать это?

Я мог бы обработать исключение в «время провода», как в следующем фрагменте:

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()

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

1 Ответ

0 голосов
/ 14 сентября 2010

Я обсуждал это в контексте 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). Что любое соединение БД может быть в любой момент признано недействительным из-за сбоя в БД или сети. Таким образом, ресурс пула соединений должен быть в состоянии справиться с выбрасыванием плохих соединений и получением новых. 3). Сбой впрыска означает, что компонент не будет запущен. Это может произойти, если, например, инъекция на самом деле является поиском JNDI. Если нет записи JNDI, мы не можем найти определение пула соединений, не можем создать пул и поэтому не можем запустить компонент. Это не то же самое, что на самом деле открытие соединения с БД ... 4). ... во время инициализации нам на самом деле не нужно открывать какие-либо соединения, отказ сделать это просто дает нам пустой пул - т.е. точно такое же состояние, как если бы мы некоторое время работали, и БД ушла, пул / мог / должен был бы выбросить устаревшие соединения.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...