python依赖注入_Python中的类型化函数依赖注入(一)

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

依赖注入是一个有争议的话题。关于如何使用DI框架,有一些已知的问题、技巧乃至一整套相关的方法论。

很多人问我:如何将许多类型化的函数概念与传统的面向对象依赖注入结合使用?

这个问题很有意义。因为函数式编程都是关于组合的。依赖注入只是个魔术。你会在代码的某些地方注入一些几乎随机的对象,之后,被注入的整个容器在运行时由各个部分神奇地组装而成。

这导致了与函数声明式风格的对立,你可能会感叹“我的天,这个类是从哪里来的?”

今天我们要用一种旧式的方法来解决这个问题。

常规函数

假设您有一个基于django的游戏,在这个游戏中,您为用户猜中的字母奖励分数(未猜到的字母标记为“.”):

以下是您的业务逻辑:

这是一个非常简单的应用程序:用户尝试猜测一些单词,同时,我们为每个猜中的字母加分。但是,我们有一个门槛。我们只对猜中6次或更多字母的情况给予6分或6分以上的奖励(猜中少于6次不奖励)。就这样。我们有框架层和我们优雅纯粹的逻辑。这段代码很容易阅读和修改。

就这样?我们试着修改一下看看。假设我们想通过设置点数阈值使我们的游戏更具挑战性。我们怎么能做到?

天真的尝试

好的,让我们使_award_points_for_letters接受第二个参数,阈值:int:

现在你的代码类型检查会失败。因为我们的调用就是这样的:

若要修复此calculate_points函数(以及所有其他上调用方函数),必须接受threshold:int作为参数,并将其传递给_award_points_for_letters,如下所示:

它还会影响我们的views.py:

我们可以看到这是可行的。但是,为了单个参数它需要更改整个调用堆栈。这就引出了一些明显的问题:

如果我们的调用堆栈很大,我们将不得不修改很多层来实现我们想要的功能,为其中每一层都增加了一个参数

我们所有的函数和类都会被我们传递的值所影响。这将有几个效果:您将无法理解哪些函数和类真正需要传递值,哪些只是作为传输层工作(如我们示例中的calculate_points)。当然,人们会在需要的地方开始使用这些传递的值。这将隐式地增加代码和环境的耦合。

很难将函数和类组合在一起。这个额外的参数总是会碍事的。函数的个数不匹配。

总而言之:这是可行的,但不是一个可扩展的解决方案。但是,在一个小程序中我可能会这么做。

框架

现在很多Django用户可能会问:为什么不像Django docs教我们的那样导入设置并使用它呢?

因为这太丑陋了!

目前,我们有独立于框架的业务逻辑。这是件好事。它免疫任何框架级别的更改。而且它也没有隐式地带来框架所有的复杂性。让我们看下django.conf.settings是如何工作的:

1.我们需要设置DJANGO_SETTINGS_MODULE环境变量来查找配置模块

2.在框架的某处会调用django.setup()

3.django将导入此模块

4.此模块可能包含依赖于设置的逻辑

5.设置还将访问环境、文件,甚至是云服务来获取一些特定值

6.然后django将拥有几乎不可变的单例对象,您可以从代码中的任何位置导入该对象

7.您必须在测试中模拟配置文件,以便将其他值传递给您的函数。或者多个(以及更多)值,如果你严重依赖配置模块(可能在任何应用程序中都是这样)

值得吗?也许吧。有时django.settings足够好了。可能只有几个基本值需要注入。这样做可以很快完成。

但是,如果你真的想构建一个边界清晰、领域逻辑全面的大型应用程序,我不建议任何人这么做。我甚至建议在您的逻辑中使用import linter来禁止从django导入任何内容。

那么,如果没有什么好用的方法,我如何将这个麻烦的值传递给函数呢?参数太嘈杂,容器太神奇,很难使用,全局设置只是披着单例形式外衣的纯粹邪恶?

组成

程序员是聪明人。真正的。他们可以用纯函数做任何事情。

他们还以优雅的方式解决了依赖性问题,同时保持了简单性。函数式依赖注入的核心思想是,我们不调用依赖于我们没有的上下文的东西。相反,我们安排他们稍后再调用。让我们看看,在采用这种想法之后,我们原来的逻辑将如何改变:

请注意,我们现在如何将factory传递到应用程序的顶层。另外,还要特别注意我们的新Deps协议:它允许我们使用结构子类型来定义所需的API。换句话说,具有WORD_THRESHOLD int属性的所有对象都将通过检查(要强制执行django设置,请使用django stubs类型检查)。

只剩下从最上面调用返回的工厂函数:

看起来很简单!我们的所有要求都得到满足:

1.我们没有魔法,真的是零

2.所有东西都输入好了

3.我们的逻辑仍然是纯粹的,独立于框架

我们现在唯一的问题是我们的逻辑结构不好。让我用一个新的要求来说明我的观点。在假期里,我们的比赛可能会随机给最后的比分加一分。下面是我们如何调整源代码以满足新的需求:

但是,awarded_points具有Callable[[[u Deps],int]类型,不能用这个新函数轻松组合。当然,我们可以在_maybe_add_extra_holiday_point里创建一个新的函数,只是为了组合:

但是这么做好吗?我希望大多数人会同意我的看法,不好。

英文原文:https://sobolevn.me/2020/02/typed-functional-dependency-injection

译者:QL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值