Crystal语言中如何实现不依赖框架的Dependency Injection?
Crystal语言无第三方框架依赖的依赖注入实现说明
Crystal标准库目前没有内置的官方依赖注入实现,不过你完全可以手动实现符合需求的DI能力,不需要依赖Athena、Shivneri等第三方框架的相关组件。
常用的零依赖实现方案
1. 构造器注入(最通用的轻量方案)
这是最无需额外代码的DI实现方式,直接通过类构造函数传入依赖即可,示例代码如下:
# 定义依赖的抽象接口 abstract class NotificationService abstract def send(message : String) : Nil end # 具体实现:邮件通知 class EmailService < NotificationService def send(message : String) : Nil puts "发送邮件通知: #{message}" end end # 具体实现:短信通知 class SmsService < NotificationService def send(message : String) : Nil puts "发送短信通知: #{message}" end end # 业务类,依赖通过构造器注入,不硬编码具体实现 class OrderService def initialize(@notification_service : NotificationService) end def create_order(order_info : String) # 执行订单创建核心逻辑 @notification_service.send("订单创建成功:#{order_info}") end end # 调用时按需传入依赖实现 # 注入邮件服务 order_service1 = OrderService.new(EmailService.new) order_service1.create_order("测试订单001") # 切换为短信服务仅需修改传入的实例,无需改动OrderService代码 order_service2 = OrderService.new(SmsService.new) order_service2.create_order("测试订单002")
2. 自定义简易DI容器
如果需要统一管理所有依赖的生命周期、自动组装依赖关系,可以自己实现一个几十行代码的轻量DI容器,完全不需要引入第三方依赖:
class DIContainer @[AlwaysInline] struct DependencyEntry getter impl : Class | Proc, singleton : Bool def initialize(@impl, @singleton) end def initialize @dependencies = {} of Symbol => DependencyEntry @singleton_instances = {} of Symbol => Any end # 注册依赖 def register(key : Symbol, impl : Class | Proc, singleton : Bool = false) @dependencies[key] = DependencyEntry.new(impl, singleton) end # 解析获取依赖实例 def resolve(key : Symbol) entry = @dependencies[key]? || raise "未注册的依赖项:#{key}" # 单例模式直接返回缓存的实例 if entry.singleton return @singleton_instances[key] if @singleton_instances.has_key?(key) end # 实例化依赖 instance = case entry.impl when Class then entry.impl.new when Proc then entry.impl.call(self) end # 单例实例写入缓存 if entry.singleton @singleton_instances[key] = instance end instance end end # 容器使用示例 container = DIContainer.new # 注册全局单例的通知服务 container.register(:notify_svc, EmailService, singleton: true) # 注册订单服务,依赖从容器中解析 container.register(:order_svc, ->(c : DIContainer) { OrderService.new(c.resolve(:notify_svc)) }) # 直接获取组装完成的订单服务实例 order_service = container.resolve(:order_svc) order_service.create_order("容器注入测试订单")
3. Setter注入(可选)
如果有运行时动态替换依赖的需求,也可以通过Setter方法实现注入,无需额外框架支持。
上述实现方式都可以覆盖绝大多数中小型项目的DI需求,只有当你需要注解自动注入、依赖自动扫描、复杂生命周期扩展等高级特性时,才需要考虑引入第三方框架的DI组件。
内容的提问来源于stack exchange,提问作者RonanDev
相关产品推荐
相关产品推荐

