You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

遵循工厂模式从控制器调用服务方法的技术问询

工厂模式在Ruby服务类中的正确实现

你提到的直接用EvaluateService.new实例化或者当前的单例实现,确实在一些场景下会有局限——比如硬编码实例化会让代码耦合度高,单例又可能带来测试不便(比如无法轻松替换实例)。下面几种工厂模式的实现方式可以解决这些问题,适配不同的场景:

1. 服务类内置的工厂方法(简单工厂)

如果你的服务逻辑相对单一,可以在服务类里提供一个清晰的工厂方法,替代直接new或者单例的evaluate_service。这种方式既保持了封装性,又比单例更灵活:

class EvaluateService
  # 私有化构造方法,强制通过工厂方法创建实例
  private_class_method :new

  def initialize
    # 初始化逻辑,比如依赖的配置、其他服务等
  end

  def get_url
    # 业务逻辑
  end

  # 工厂方法:对外暴露的实例创建入口
  def self.create
    new
  end

  # 如果确实需要复用实例(类似单例但更可控),可以用带参数的工厂方法
  def self.instance
    @instance ||= create
  end
end

# 在控制器中调用
class CheckController < ApplicationController
  def index
    evaluate_service = EvaluateService.create
    # 或者如果需要单例:evaluate_service = EvaluateService.instance
    url = evaluate_service.get_url
  end
end

这里把new设为私有,强制外部通过create或instance获取实例,既规范了实例化方式,也方便后续修改实例创建逻辑(比如需要传入初始化参数时,只改工厂方法就行)。

2. 独立的工厂类(工厂方法模式)

如果你的系统中有多个类似的服务类(比如EvaluateService、ValidateService),或者服务的创建逻辑比较复杂(比如需要根据不同参数创建不同子类实例),可以用独立的工厂类来统一管理实例创建:

# 定义服务接口(Ruby中用模块或父类实现)
module Service
  def get_url; end
end

class EvaluateService
  include Service

  def initialize(config)
    @config = config
  end

  def get_url
    # 业务逻辑
  end
end

class ValidateService
  include Service

  def initialize(config)
    @config = config
  end

  def get_url
    # 另一种业务逻辑
  end
end

# 独立的工厂类
class ServiceFactory
  def self.create(service_type, config)
    case service_type
    when :evaluate
      EvaluateService.new(config)
    when :validate
      ValidateService.new(config)
    else
      raise ArgumentError, "Unknown service type: #{service_type}"
    end
  end
end

# 控制器中调用
class CheckController < ApplicationController
  def index
    config = { api_key: ENV['API_KEY'] }
    evaluate_service = ServiceFactory.create(:evaluate, config)
    url = evaluate_service.get_url
  end
end

这种方式把实例创建的逻辑从业务类中分离出来,降低了耦合,也让扩展新服务变得更简单——只需要新增服务类和工厂类中的case分支即可。

3. 结合依赖注入的工厂模式(适合测试友好的场景)

如果你的服务有外部依赖(比如数据库连接、其他API客户端),可以用工厂模式配合依赖注入,让测试时能轻松替换依赖:

class EvaluateService
  def initialize(url_fetcher)
    @url_fetcher = url_fetcher
  end

  def get_url
    @url_fetcher.fetch
  end
end

# 工厂类负责注入默认依赖
class EvaluateServiceFactory
  def self.create
    default_fetcher = DefaultUrlFetcher.new
    EvaluateService.new(default_fetcher)
  end

  # 测试时用的工厂方法,允许传入自定义依赖
  def self.create_with_fetcher(fetcher)
    EvaluateService.new(fetcher)
  end
end

# 控制器中使用默认实例
class CheckController < ApplicationController
  def index
    evaluate_service = EvaluateServiceFactory.create
    url = evaluate_service.get_url
  end
end

# 测试代码示例
RSpec.describe EvaluateService do
  it "returns the correct url" do
    mock_fetcher = double("UrlFetcher", fetch: "https://example.com")
    service = EvaluateServiceFactory.create_with_fetcher(mock_fetcher)
    expect(service.get_url).to eq("https://example.com")
  end
end

这种方式让服务类的依赖更清晰,测试时可以用mock对象替换真实依赖,极大提升了代码的可测试性。

总结一下,选择哪种工厂模式取决于你的业务场景:

  • 简单服务用内置工厂方法即可,简洁又规范;
  • 多服务场景用独立工厂类,统一管理创建逻辑;
  • 注重测试和可维护性的场景,优先考虑结合依赖注入的工厂模式。

内容的提问来源于stack exchange,提问作者Kingsley Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:50:45