向符合共同接口的适配器注入适配器的pythonic方法



我正在使用boto3设置一些AWS路由。

在AWS环境中,我们希望有一个负责将路线戳入EC2 VPC的课程。在非AWS环境中,它将做其他事情。

在传统的OOP方法中,我将具有一个定义announce_route方法的接口,并创建AwsRouting或FoobarRouting的具体实例等。

伪代码说明方案:

interface Routing
   function announceRoute()
class AWSRouting implements Routing
class RouteAnnouncer
   function constructor(Routing routing)

我会根据某些环境变量或配置选项将适当的路线注入播音员。

用依赖注入来实现这种适配器模式的Pythonic方法是什么?到目前为止


这就是我到目前为止所拥有的:

class RouteAnnouncer:
    def __init__(self, adapter):
        self.adapter = adapter
    def announce_route(self, ip_addr):
        self.adapter.announce_route(ip_addr)

class AnnounceRouteContract:
    def announce_route(self, ip_addr):
        raise NotImplementedError

class AWSRouting(AnnounceRouteContract):
    def announce_route(self, ip_addr):
        pass

class StandardRouting(AnnounceRouteContract):
    def announce_route(self, ip_addr):
        pass

所以我认为我真正缺少的是如何告诉用户RouteAnnouncer中的适配器是AnnounceRouteContract的实例。

您不需要做几乎如此愚蠢的事情,例如创建一堆"接口"。在Python中,您的界面取决于您的行为方式。通常称为"鸭子打字",因为如果您看起来像鸭子,您像鸭子一样行走,然后像鸭子一样颤抖,那么您必须是鸭子。

我们不在乎您是是否真的 穿着鸭子服装的人,还是鸭子的纸板切口或机器人鸭。这完全是无关的。

在您的情况下,您的全部真正关心的是announce_route(ip_addr)是您可以调用的东西。当然,最终您希望它将真正设置路线,但是您知道,这就是测试的目的。

May 想做的就是拥有一个看起来像这样的工厂:

class MyRouteAnnouncer:
    def __init__(self):
        ...  # whatever you need to do
    def announce_route(self, ip_addr):
        ...  # do what you need to do

def get_router():
    if env.is_aws():
        return boto3
    elif env.is_something_else():
        return MyRouteAnnouncer()
    else:
        raise Exception('Unknown environment')

,然后任何称为get_router()功能的函数都会获取适当的路由器并在其上调用announce_route(ip_addr)

您不能强迫类用户仅将本机Python的特定类实例放置(在Python 3.6中似乎已实现)。您可以为用户提供一个提示,以便IDE如果没有预期的类别的实例,可以抱怨:

class RouteAnnouncer:
    def __init__(self, adapter):
        """
        :param AnnounceRouteContract adapter: Route adapter
        self.adapter = adapter

还制作合同摘要以及announce_route的方法来强制用户实施它。

您也可以使用ISSUBCLASS内置功能检查适配器是否为预期合同的实例

在Python World中,无法指定参数类型。我认为一种适当的方法是使用断言在内部函数中检查适配器类型。

class RouteAnnouncer:
    def __init__(self, adapter):
        assert isinstance(adapter, AnnounceRouteContract)
        self.adapter = adapter

最新更新