如何在依赖注入期间处理错误和异常

时间:2010-09-14 14:47:42

标签: exception-handling dependency-injection

据我所知,依赖注入将应用程序布线逻辑与业务逻辑分开。此外,我试图通过注入直接合作者来遵守得墨忒耳的法律。

如果我正确理解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()

这里的缺点是提供商增加了复杂性并违反了得墨忒耳的法律。

在使用文章中讨论的依赖注入框架时,处理这类异常的最佳方法是什么?


SOLUTION ,基于与djna的讨论

首先,正如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()

请注意,我已在最后一个片段中剪切了一些角落。有关输入范围的更多信息,请参阅上述文章。

1 个答案:

答案 0 :(得分: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连接都可能因DB或网络故障而随时失效。因此,连接池资源必须能够处理丢弃不良连接并获取新连接。 3)。注入失败意味着组件将无法启动。例如,如果注入实际上是JNDI查找,则可能发生这种情况。如果没有JNDI条目,我们找不到连接池定义,无法创建池,因此无法启动组件。这与实际打开与DB的连接不同... 4)。 ...在初始化时我们实际上并不需要打开任何连接,如果不这样做只会给我们一个空池 - 即。完全相同的状态,好像我们已经运行了一段时间,数据库消失了,池将/可能/应该扔掉陈旧的连接。

这个模型似乎很好地定义了Demeter可能接受的一系列职责。注射已经准备好准备地面,确保当代码需要做一些事情时它可以。代码有责任收获水果,尝试使用准备好的材料并应对实际资源故障,并反对未能找到资源。