MST

星途 面试题库

面试题:Ruby中依赖注入框架的原理剖析与自定义实现

分析Ruby中常见依赖注入框架(如Dry::Container)的工作原理,包括如何管理对象生命周期、处理依赖关系等。然后,基于Ruby语言特性,尝试自定义一个简单的依赖注入框架,要求实现基本的对象注册、解析以及依赖注入功能,并说明设计思路。
12.2万 热度难度
编程语言Ruby

知识考点

AI 面试

面试题答案

一键面试

Dry::Container工作原理分析

  1. 对象生命周期管理
    • Dry::Container使用惰性加载机制。当对象被注册后,只有在实际需要解析(获取)该对象时才会创建它。例如,假设注册了一个数据库连接对象:
    require 'dry/container'
    container = Dry::Container.new
    container.register(:db_connection) do
      require 'pg'
      PG.connect(dbname: 'test')
    end
    
    这里db_connection对象只有在调用container[:db_connection]时才会真正创建,这样可以避免在应用启动时就创建大量可能不需要的对象,提高启动性能。
    • 对于单例对象,Dry::Container提供了专门的方法进行管理。比如:
    container.register(:config, singleton: true) do
      require 'yaml'
      YAML.load_file('config.yml')
    end
    
    这样无论多少次调用container[:config],返回的都是同一个实例,保证了配置对象在整个应用中的一致性。
  2. 处理依赖关系
    • Dry::Container支持依赖的自动解析。当一个对象的创建依赖于其他对象时,容器可以自动处理这些依赖。例如:
    container.register(:user_repository) do
      require_relative 'user_repository'
      UserRepository.new(container[:db_connection])
    end
    
    这里user_repository的创建依赖于db_connection,容器在解析user_repository时,会先确保db_connection被创建并注入到UserRepository的构造函数中。

自定义简单依赖注入框架

  1. 设计思路
    • 首先,我们需要一个容器类来管理所有注册的对象。这个容器类应该有方法来注册对象和解析对象。
    • 对于对象注册,我们可以使用一个哈希表来存储注册的对象及其创建逻辑。
    • 在解析对象时,如果对象的创建依赖其他对象,我们递归地解析这些依赖,确保所有依赖对象都被正确创建并注入。
  2. 代码实现
class MyDependencyInjector
  def initialize
    @registry = {}
  end

  def register(key, &block)
    @registry[key] = block
  end

  def resolve(key)
    raise "Object not registered: #{key}" unless @registry.key?(key)
    block = @registry[key]
    dependencies = block.parameters.map { |_, name| resolve(name) }
    block.call(*dependencies)
  end
end

可以这样使用:

injector = MyDependencyInjector.new
injector.register(:db_connection) do
  require 'pg'
  PG.connect(dbname: 'test')
end
injector.register(:user_repository) do |db_connection|
  require_relative 'user_repository'
  UserRepository.new(db_connection)
end
user_repo = injector.resolve(:user_repository)

在这个实现中,register方法用于注册对象及其创建逻辑。resolve方法负责解析对象,如果对象依赖其他对象,会先递归解析依赖对象并注入到创建逻辑中。