Exception handling 如何处理依赖项注入期间的错误和异常

Exception handling 如何处理依赖项注入期间的错误和异常,exception-handling,dependency-injection,Exception Handling,Dependency Injection,据我所知,依赖注入将应用程序连接逻辑与业务逻辑分离。此外,我试图通过只注入直接合作者来遵守德米特定律 如果我理解正确,正确的依赖项注入意味着在注入协作者时应该完全初始化它们,除非需要延迟实例化。这意味着,而且实际上在文章中提到,像数据库连接和文件流这样的对象应该在注入时启动并准备就绪 但是,打开文件和连接可能会导致异常,应该在某个时候进行处理。最好的办法是什么 我可以在“连线时间”处理该异常,如以下代码段所示: class Injector: def inject_MainHelper

据我所知,依赖注入将应用程序连接逻辑与业务逻辑分离。此外,我试图通过只注入直接合作者来遵守德米特定律

如果我理解正确,正确的依赖项注入意味着在注入协作者时应该完全初始化它们,除非需要延迟实例化。这意味着,而且实际上在文章中提到,像数据库连接和文件流这样的对象应该在注入时启动并准备就绪

但是,打开文件和连接可能会导致异常,应该在某个时候进行处理。最好的办法是什么

我可以在“连线时间”处理该异常,如以下代码段所示:

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()
这里的缺点是增加了提供者的复杂性,并且违反了Demeter法

在使用本文讨论的依赖项注入框架时,处理此类异常的最佳方法是什么

解决方案,基于与djna的讨论

首先,正如djna正确指出的那样,在我的第一个解决方案中没有实际的业务和布线逻辑的混合。布线发生在它自己的独立类中,与其他逻辑隔离

第二,有范围的情况。有两个较小的作用域,而不是一个:

尚未验证文件的作用域。在这里,注入引擎还不能假设任何关于文件状态的信息,也不能构建依赖于它的对象。 成功打开和验证文件的范围。在这里,注入引擎可以基于提取的文件内容创建对象,而无需担心文件错误。 在进入第一个范围并获得关于打开和验证文件的足够信息后,业务逻辑将尝试实际验证和打开文件,以收获果实,正如djna所说的那样。在这里,可以相应地处理异常。当确定文件已正确加载和解析时,应用程序可以进入第二个作用域

第三,与核心问题无关,但仍然是一个问题:第一个解决方案将业务逻辑嵌入主循环,而不是mainheloper。这使得测试更加困难

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

请注意,我在最后一段中做了一些删节。有关进入作用域的更多信息,请参阅上述文章。

我在JavaEE、EJB3和参考资料的上下文中对此进行了讨论

我的理解是,我们需要区分资源引用的注入和资源的实际使用

以数据库连接为例,我们有一些伪代码

 InjectedConnectionPool icp;

 public void doWork(Stuff someData) throws Exception{

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


 }
据我了解:

一,。注入的资源不能是连接本身,而必须是连接池。我们在短时间内获取连接并返回它们。 2.任何数据库连接可能在任何时候因数据库或网络故障而失效。因此,连接池资源必须能够处理丢弃坏连接和获取新连接的问题。 3.注入失败意味着部件将无法启动。例如,如果注入实际上是一个JNDI查找,就可能发生这种情况。如果没有JNDI条目,我们将无法找到连接池定义,无法创建池,因此无法启动组件。这与实际打开到数据库的连接不同。。。 4.在初始化时,我们实际上不需要打开任何连接,如果不打开,只会给我们一个空池-即,与我们运行了一段时间并且数据库消失时的状态完全相同,池将/可以/应该丢弃陈旧的连接


这个模型似乎很好地定义了德米特可能接受的一系列责任。Injection具有准备基础的能力,确保当代码需要做某些事情时,它可以。代码有责任收获果实,尝试使用准备好的材料,处理实际的资源故障,而不是查找资源的故障

我可以看出,在这种情况下,连接池中的连接是如何合适的,但这会导致实际需要一个“连接”的情况。例如,在应用程序运行时开始加载和解析配置文件时。如果您必须作为注入的一部分执行实际工作,则可以应用相同的规则-如果您无法完成注入,则应用程序无法启动。因此,责任划分也很明确。所以有两种选择:不启动,或者要求应用程序代码能够适应持续的故障。我认为,你所描述的那种纯粹的初始化失败,应该会导致启动失败。那么,最好的方法是什么呢
他的我的第一个解决方案在“所有其他业务逻辑之外”处理此类异常,但在我看来,这就像是混合了布线和业务逻辑。如果发动机遇到异常,它知道停止。它被告知:注入这个文件、数据库连接或其他任何东西。这里面没有业务逻辑,只是一个资源访问。我看不出这里有混合。你是对的,我被异常处理的位置分散了注意力。根据你的提示,我制定了一个解决方案。非常感谢。