前记
由于使用的主要编程语言是Python
,所以对于依赖注入这个概念并不是很清楚或者不知道自己已经在代码中运用了依赖注入的用法,在接触了DDD后才开始真正的了解什么是依赖注入以及依赖注入的重要性,同时也很好奇为何在Python
生态中依赖注入的出现率较低。
1.控制反转与依赖注入
在查找依赖注入的相关资料时发现依赖注入与控制反转这两个词是成对出现的,这是因为控制反转并不是一种技术,而是一种编程思想,这种思想能指导我们如何设计出松耦合优良的程序,而依赖注入是一个具体的设计模式,它是控制反转的一种具体实现。
控制反转这个概念有点模糊,但后端开发者来说却是经常接触到的,比如在对框架与库的使用时,分别接触到他们的反向控制和正向控制,而框架的反向控制正是控制反转的体现。 大多数人很少去区分在项目中使用的包是属于框架还是库,也不会很清晰的去区分它们属于哪一种,我也是这样的,在经过了一段编程生涯后我才可以简单的把主动调用的包归类为库,只能被动调用的包归类为框架。
不过最近在翻阅依赖注入与控制反转的相关文章InversionOfControl时发现,控制反转也可以是框架和库的关键区别点。因为对于一个库来说,程序员使用的方式是主动的调用它,如下代码:
import httpx
response = httpx.get("https://so1n.me")
print(response.status_code)
这段代码主动的调用httpx
包的get
方法发起一个请求以获取网站对应的状态码,由于这种调用方法属于开发者去主动调用库,所以属于正向的控制。
而框架就不一样了,框架一般都会提供一些注册的方法将我们编写的代码注册到框架中,最后由框架来调用程序员编写的代码,如下例子:
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse
def demo(request: Request) -> PlainTextResponse:
return PlainTextResponse(f"Hello {request.query_params.get('name', '')}!")
def create_app() -> Starlette:
app: Starlette = Starlette()
app.add_route("/", demo, methods=["GET"])
return app
if __name__ == "__main__":
import uvicorn
app: Starlette = create_app()
uvicorn.run(app)
这段代码中先是声明一个路由函数demo
,这个路由函数是按照框架要求的方式编写的,这个要求是路由函数必须接收一个Request
参数以及返回一个Response
类;接着在实例化框架时通过add_route
方法以path=/
,method=GET
的形式注册到框架中以及在调用uvicorn.run(app)
的时候把控制权转移给了框架,并由框架在后续完成对demo
路由函数的调用,这种调用方式属于反向控制。
Note: 对于流式客户端封装的库可能包含着主动调用与被动调用,使其不像框架也不像库。
此外,从例子中可以看到,创建的demo
路由函数是交给了框架控制的,不再由开发者控制,而且demo
路由函数接收的Request
类参数是在运行时由框架管理创建和注销并注入给路由函数供路由函数使用的,这就是控制反转的主体思想,通过这种思想能公减少工程项目不同层次代码打耦合。
依赖注入则是控制反转的一种具体实现方式,这种方式能让一个对象接收它所依赖的其他对象。其中“依赖”是指接收方所需的对象,“注入”是指将“依赖”传递给接收方的过程。在“注入”之后,接收方才会调用该“依赖”。 而依赖注入框架则是一种根据对象的依赖关系的在运行时进行绑定的技术,通常它都会带有一个容器,这个容器托管着许多对象,并在运行时根据对象的依赖关系把对象传递给被控制的其它对象中。比如例子中的starlette
框架在运行时就是一个容器,它可以根据不同的请求创建不同的请求对象并在根据请求规则匹配到对应的路由函数后把请求