一文深入理解Python中的依赖注入

2023-05-2616:09:50后端程序开发Comments1,239 views字数 4363阅读模式
一文深入理解Python中的依赖注入

简介

依赖注入是软件工程中使用的一种设计模式,它允许在创建对象时由外部提供其依赖关系,而不是自己创建这些依赖关系。换句话说,不是从类创建自己的依赖关系,而是将依赖关系从外部注入到该类。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

一文深入理解Python中的依赖注入

为什么使用依赖注入?

依赖注入的好处包括提高软件设计的灵活性和模块化,改善可测试性,以及减少组件之间的耦合。通过将对象与它们的依赖关系解耦,可以更容易地进行更改而不影响系统的其他部分。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

例如,假设有一个依赖于数据库连接的类。如果在该类中创建数据库连接,就在该类和数据库之间建立了一个紧密的耦合。这意味着对数据库连接的任何更改都需要对类进行修改,这使得代码的灵活性降低,更难维护。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

通过依赖注入,可以从外部将数据库连接传递给类,使代码更加模块化,更容易测试。这也允许用不同的实现来替换数据库连接,比如与不同的数据库或外部API进行交互,而不需要修改类本身。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

用Python实现依赖注入

Python是一种流行的编程语言,支持几种实现依赖注入的方法。本文将使用构造函数注入来演示这一概念。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

构造函数注入包括通过构造函数将依赖关系传递给一个类。这允许该类将依赖关系存储为实例变量,使它们对其方法可用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

创建一个例子来演示它是如何工作的。将从定义一个依赖于UserRepository接口的UserService类开始:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

class UserService:
    def __init__(self, user_repository):
        self.user_repository = user_repository
    def get_user(self, user_id):
        return self.user_repository.get_user(user_id)

在此示例中,UserService类在其构造函数中把UserRepository对象作为一个参数。然后它将UserRepository对象存储为一个实例变量,并在其get_user方法中使用它。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

可以通过给UserRepository添加一个get_user方法来定义它的接口:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

class UserRepository:
    def get_user(self, user_id):
        raise NotImplementedError

这创建了一个简单的接口,UserRepository类的任何实现都必须实现。在这种情况下将创建一个UserRepository类的实现,叫做InMemoryUserRepository,它将用户数据存储在内存中:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

class InMemoryUserRepository(UserRepository):
    def __init__(self):
        self.users = {
            1: {"id": 1, "name": "Alice"},
            2: {"id": 2, "name": "Bob"},
            3: {"id": 3, "name": "Charlie"},
        }
    def get_user(self, user_id):
        return self.users.get(user_id, None)

InMemoryUserRepository类中,定义了一个包含用户数据的字典,并实现了get_user方法,通过ID检索用户。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

最后,在主函数中,创建了一个InMemoryUserRepository的实例,并将其传递给UserService的构造函数。然后在UserService实例上调用get_user方法,通过他们的ID检索用户:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

if __name__ == "__main__":
    user_repository = InMemoryUserRepository()
    user_service = UserService(user_repository)
    user = user_service.get_user(1)

在此示例中,创建一个InMemoryUserRepository的实例,它实现了UserRepository接口。然后创建一个UserService的实例,将InMemoryUserRepository实例作为参数传给它的构造函数。最后,在UserService实例上调用get_user方法,通过ID检索一个用户。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

通过使用构造函数注入,可以很容易地将UserRepository的实现换成不同的实现,比如与数据库或外部API交互的实现,而不需要修改UserService类本身。这使得代码更灵活,更容易维护。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

如下所示是完整的InMemoryUserRepository.py文件:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

from UserRepository import UserRepository
from UserService import UserService

class InMemoryUserRepository(UserRepository):
    def __init__(self):
        self.users = {
            1: {"id": 1, "name": "Alice"},
            2: {"id": 2, "name": "Bob"},
            3: {"id": 3, "name": "Charlie"},
        }

    def get_user(self, user_id):
        return self.users.get(user_id, None)

if __name__ == "__main__":
    user_repository = InMemoryUserRepository()
    user_service = UserService(user_repository)
    user = user_service.get_user(1)
    print(user.get("name"))

接下来的例子展示了如何用DatabaseUserRepository的实现来替换InMemoryUserRepository的实现:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

import os
import sqlite3
from UserRepository import UserRepository
from UserService import UserService

class DatabaseUserRepository(UserRepository):
    def __init__(self, db_path):
        self.db_path = db_path

    def get_user(self, user_id):
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, name FROM users WHERE id=?", (user_id,))
            row = cursor.fetchone()
            if row is None:
                return None
            return {"id": row[0], "name": row[1]}

if __name__ == "__main__":
    # 用`DatabaseUserRepository`来替换`InMemoryUserRepository`
    db_dir = "test"
    os.makedirs(db_dir, exist_ok=True)
    db_path = os.path.join(db_dir, "test.db")
    
    with sqlite3.connect(db_path) as conn:
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
        cursor.execute('INSERT INTO users (id, name) VALUES (1, "Alice")')
        cursor.execute('INSERT INTO users (id, name) VALUES (2, "Bob")')
        cursor.execute('INSERT INTO users (id, name) VALUES (3, "Charlie")')
    
    user_repository = DatabaseUserRepository(db_path)
    user_service = UserService(user_repository)
    user = user_service.get_user(1)
    print(user["name"])

在此示例中,os模块被用来在当前工作目录中创建一个名为“test”的新目录(如果它还不存在的话),并且通过连接目录路径和文件名“test.db”来创建SQLite数据库文件的路径。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

然后建立与数据库的连接,创建一个用户表,并将一些测试数据插入该表中。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

最后,创建一个DatabaseUserRepository类的实例,db_path参数设置为测试数据库文件的路径,这个实例被用来创建UserService类的实例。然后调用UserService类的get_user()方法,参数为1,ID为1的用户的名字被打印到控制台。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

本文的所有代码都可以在Github上找到:https://github.com/PythonCodeNemesis/Python_Dependancy_Injector_Demo文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

总结

依赖注入是一种强大的设计模式,可以帮助使软件更加模块化、更加灵活、更加容易测试。Python提供了几种实现依赖注入的方法,包括构造函数注入,在本文中演示了这种方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

通过使用依赖注入,可以创建与依赖关系解耦的类,使修改和维护代码更加容易。这也可以提高软件的整体质量,并使其更有弹性地应对长期的变化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/42642.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/42642.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定