首页
/ Python-Dependency-Injector中的默认工厂模式实现

Python-Dependency-Injector中的默认工厂模式实现

2025-06-14 06:48:13作者:郦嵘贵Just

在Python依赖注入框架Python-Dependency-Injector中,FactoryAggregate和Selector是两种常用的提供器(Provider)类型,它们允许开发者根据不同的条件选择不同的依赖实现。然而,这两种提供器默认情况下不支持设置"默认工厂"选项,当传入未定义的选项时会抛出NoSuchProviderError异常。

问题背景

在实际开发中,我们经常需要处理这样的情况:当用户传入一个未预定义的选项时,系统应该返回一个默认实现而不是直接报错。例如,在一个多语言支持的系统中,当用户请求的语言不支持时,我们希望返回默认语言的内容。

原生解决方案的局限性

Python-Dependency-Injector原生提供了两种处理方式:

  1. FactoryAggregate:通过字典形式定义多个工厂,但不支持默认选项
  2. Callable提供器:可以编写自定义逻辑处理所有情况,但代码结构不如FactoryAggregate清晰

自定义DefaultFactoryAggregate提供器

为了弥补这一功能缺失,我们可以通过继承或组合的方式创建自定义提供器。以下是两种实现方式:

组合方式实现

class DefaultFactoryAggregate(providers.Provider):
    DEFAULT_OPTION = '_'
    __slots__ = ('_agg_factory')

    def __init__(self, provider_dict=None, **provider_kwargs):
        self._agg_factory = providers.FactoryAggregate(provider_dict, **provider_kwargs)
        super().__init__()
        
    @property
    def related(self):
        """返回相关提供器生成器"""
        yield from [self._agg_factory]
        yield from super().related

    def __deepcopy__(self, memo):
        copied = memo.get(id(self))
        if copied is not None:
            return copied

        copied = self.__class__(
            self._agg_factory.providers,
        )
        self._copy_overridings(copied, memo)
        return copied

    def _provide(self, args, kwargs):
        try:
            return self._agg_factory(*args, **kwargs)
        except NoSuchProviderError:
            kwargs.pop('factory_name', None)
            args = (self.DEFAULT_OPTION,) + args[1:]
            return self._agg_factory(*args, **kwargs)

继承方式实现

class InheritedDefaultFactoryAggregate(providers.FactoryAggregate):
    DEFAULT_OPTION = '_'
    
    def _provide(self, args: tuple, kwargs: dict):
        try:
            return super()._provide(args, kwargs)
        except NoSuchProviderError:
            kwargs.pop('factory_name', None)
            args = (self.DEFAULT_OPTION,) + args[1:]
            return super()._provide(args, kwargs)

实现原理分析

这两种实现方式都遵循了相同的核心逻辑:

  1. 首先尝试使用用户传入的参数调用原始FactoryAggregate
  2. 如果抛出NoSuchProviderError异常,则使用默认选项重新尝试
  3. 在重试前清理可能存在的factory_name参数
  4. 将第一个参数替换为默认选项标识

组合方式更符合"组合优于继承"的设计原则,但代码量较大;继承方式更为简洁,但可能带来潜在的继承问题。

使用示例

class C(DeclarativeContainer):
    agg_factory = DefaultFactoryAggregate(
        option1=providers.Factory(Obj, 'option1'),
        option2=providers.Factory(Obj, 'option2'),
        _=providers.Factory(Obj, "I'm default")
    )

container = C()
print(container.agg_factory('option1'))  # <Obj name=option1>
print(container.agg_factory('random'))   # <Obj name=I'm default>

最佳实践建议

  1. 命名约定:建议使用"_"作为默认选项的标识符,保持一致性
  2. 错误处理:考虑添加日志记录,记录默认选项被使用的情况
  3. 性能考虑:异常处理会有一定性能开销,在性能敏感场景需谨慎使用
  4. 测试覆盖:确保测试用例覆盖默认选项分支

通过这种自定义提供器,我们可以在保持FactoryAggregate清晰结构的同时,获得更灵活的错误处理能力,使代码更加健壮和用户友好。

登录后查看全文
热门项目推荐
相关项目推荐