Rust 程序设计:三层架构的“全局数据”的“依赖注入”设计方法

news/2024/9/22 21:35:52/

依赖注入

Rust 程序分三层:api、logic、data,其中数据 Server 在 data 中定义,如果在 data 内定义成全局静态变量,api 层可以看不到 Server,但是 data层的代码访问全局变量 Server 必须是 unsafe 的。

如果 Server 在 api 用 Arc<Mutex<Server>> 形式,则可以在 main 中定义并传递到 logic 、data 处理。但这样做似乎违背了 api 层不可访问 data 层信息的原则。其实,这样做并不违反三层架构原则,因为 main 函数并非三层架构中的 api 层,而是承担起了三层架构的组织者的角色 —— 这就是所谓的“依赖注入” 方法。

是的,依赖注入方法确实允许main函数访问并操作data层的信息。在依赖注入架构中,main函数通常负责初始化程序的主要组件,并将它们组装在一起。这意味着main函数会创建并配置数据层所需要的资源,如数据库连接、服务器实例等,并将这些资源注入到需要它们的组件中。

虽然main函数可以访问和操作data层的信息,但这并不违反封装原则,因为main函数是程序的入口点,它需要负责程序的初始化和配置。重要的是要确保除了main函数(或其他负责初始化的部分)之外,其他模块和层不应该直接访问data层的信息,以保持代码的模块化和封装性。

在实际操作中,main函数会创建Arc<Mutex<Server>>(或其他共享资源)的实例,并将其传递给logic层。Logic层随后可以使用这个共享资源来完成其逻辑处理,而无需关心资源的具体来源或实现细节。这样,即使main函数能够访问和操作data层的信息,也不会破坏整体的封装性,因为其他模块并不直接依赖于data层的具体实现。

简而言之,依赖注入允许main函数在初始化阶段访问和操作data层的信息,以确保程序能够正确地配置和启动。然而,在程序运行期间,其他模块应该通过接口或抽象层与data层进行交互,以保持代码的封装性和模块化。

依赖注入”是架构原则还是设计模式?

依赖注入Dependency Injection,简称DI)既可以说是一种软件架构的原则,也可以说是一种设计模式。下面分别从这两个角度来解释依赖注入

  1. 作为软件架构原则

    • 依赖注入是控制反转(Inversion of Control,IoC)原则的一个具体实现方式。在传统的软件开发中,程序的各个组件往往自己负责创建或查找它们所需要的依赖。而在IoC原则下,这种控制权被“反转”了,由框架或容器来负责管理对象的生命周期、依赖关系等。依赖注入作为IoC的一种实现方式,它强调通过外部方式(例如构造函数、属性设置或接口)来将依赖对象“注入”到需要它们的对象中,而不是在被依赖对象内部创建或查找这些依赖。
  2. 作为设计模式

    • 依赖注入也是一种设计模式,用于减少类之间的耦合度。通过将依赖关系从硬编码中解耦出来,使得代码更加灵活、可测试和可维护。在设计模式中,依赖注入通常通过三种方式实现:构造函数注入、属性注入和接口注入。这些方式都旨在将依赖的创建和查找责任从使用它们的类中分离出来。

总的来说,依赖注入既可以视为软件架构中的一个重要原则,也可以看作是一种具体的设计模式。它主要通过外部注入依赖的方式来减少类之间的耦合度,提高代码的可测试性和可维护性。这种方法广泛应用于各种编程语言和框架中,是现代软件开发中的一个重要概念。

Rust 程序示例

在Rust中,依赖注入通常通过结构体和trait来实现。下面是一个简单的Rust依赖注入示例:

rust">// 定义一个发送邮件的trait
trait EmailSender {fn send_email(&self, message: &str);
}// 真实的EmailSender实现
struct RealEmailSender {}impl EmailSender for RealEmailSender {fn send_email(&self, message: &str) {println!("Sending email with message: {}", message);}
}// 模拟的EmailSender实现,可能用于测试
struct MockEmailSender {}impl EmailSender for MockEmailSender {fn send_email(&self, message: &str) {println!("Mock sending email with message: {}", message);}
}// MessageService结构体,它依赖于一个实现了EmailSender trait的对象
struct MessageService<T: EmailSender> {email_sender: T,
}impl<T: EmailSender> MessageService<T> {// 使用泛型和trait bound来构造MessageService,注入EmailSender的实例fn new(email_sender: T) -> Self {Self { email_sender }}// 发送消息的方法fn send_message(&self, message: &str) {self.email_sender.send_email(message);}
}fn main() {// 创建一个真实的EmailSender实例let real_email_sender = RealEmailSender {};// 通过构造函数注入EmailSender的实例来创建MessageServicelet real_message_service = MessageService::new(real_email_sender);// 使用MessageService发送消息real_message_service.send_message("Hello, this is a real message!");// 创建一个模拟的EmailSender实例,可能用于测试let mock_email_sender = MockEmailSender {};// 通过构造函数注入模拟的EmailSender实例来创建另一个MessageServicelet mock_message_service = MessageService::new(mock_email_sender);// 使用模拟的MessageService发送消息mock_message_service.send_message("Hello, this is a mock message!");
}

在这个示例中,我们定义了一个EmailSender trait,它有一个send_email方法。然后我们创建了两个实现了这个trait的结构体:RealEmailSender用于实际发送电子邮件,而MockEmailSender可能用于测试环境。

MessageService结构体被设计为泛型,并带有一个trait bound,要求传入的类型T必须实现EmailSender trait。这样,我们可以将任何实现了EmailSender的类型注入到MessageService中。

main函数中,我们分别创建了使用真实和模拟发送器的MessageService实例,并演示了如何使用它们发送消息。这种设计允许我们轻松地替换依赖项以进行测试或适应不同的环境。


http://www.ppmy.cn/news/1462817.html

相关文章

请说说 Java中 static 修饰符是干啥的?

好的,面试官. static英文含义是静态的,也就是在修饰成员(对象,方法,代码块,变量)时,表明他们都是属于静态成员 其次被修饰的成员有几大特性: 1.一个被static修饰的静态成员不再属于实例出来的对象,而是只属于这个类自己. 2.访问static修饰的成员要通过类名访问,在类加载时初…

大语言模型的创意“魔法“:召唤隐藏的联想思维

随着人工智能的迅猛发展&#xff0c;大语言模型正在掀起一场"创意风暴"。这些强大的AI模型不仅能够生成栩栩如生的文本&#xff0c;还展现出惊人的创造力。但你是否好奇&#xff0c;它们的创意究竟来自何处? 最新研究表明&#xff0c;大语言模型的创意之源在于激活…

二叉树——进阶(递归创建,非递归,广度优先,翻转,深度,对称)

二叉树——进阶 二叉树的递归创建非递归前中后序遍历非递归前序遍历非递归中序遍历非递归后序遍历 广度优先遍历二叉树&#xff08;层序遍历&#xff09;翻转二叉树 二叉树深度最大深度最小深度 对称二叉树 二叉树的递归创建 1&#xff0c;二叉树是一种结构相对固定的数据&…

ElasticSearch操作之重置密码脚本

ElasticSearch操作之重置密码脚本 #!/bin/bash # 使用样例 ./ES密码重置.sh 旧密码 新密码# 输入旧密码 es_old_password$1# 设置新的密码变量 es_password$2# 正确响应 es_reponse{"acknowledged":true}# 检查Elasticsearch是否在运行 if pgrep -f elasticsearch &g…

JAVA面试题大全(四)

一、什么是反射&#xff1f; java反射机制是指在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff0c;对于任意一个对象&#xff0c;都能够调用它的任意一个方法和属性&#xff0c;这种动态获取的信息以及动态调用对象的方法的功能…

在Android的WindowManager中添加ComposeView的流程及注意事项

在Android的WindowManager中添加ComposeView的流程及注意事项 流程 总体跟普通的View一样设置窗口参数再用addView添加到WIndowManager中 private val wm by lazy { MobilePlatformPlugin.applicationContext.getSystemService(WINDOW_SERVICE) as WindowManager }fun showPo…

OOM不会导致JVM退出

问题来源 一次生产事故&#xff0c;由于一次性从数据库查询过多数据导致线程 OOM&#xff1a;Java heap space 异常&#xff08;千万级表&#xff0c;JVM堆内存2G&#xff09;&#xff0c;但是在线程OOM发生时&#xff0c;java进程却没有立即挂掉。 ##OOM与异常 说到底OutOfM…

阿尔杰姆·卢金采访

近年来&#xff0c;在中俄两国元首亲自擘画、战略引领下&#xff0c;两国新时代全面战略协作伙伴关系成熟坚韧、稳如泰山&#xff0c;树立了新型大国关系的新范式。中俄关系走出了一条大国战略互信、邻里友好的相处之道&#xff0c;给两国人民带来了实实在在的好处&#xff0c;…