两步施工。有用还是只是代码味道?

Two step construction. Useful or just a code smell?

通常我希望一旦一个对象被构建,它就应该可以使用了,就这样。没有两步施工。如果您需要调用两个构造函数来使用一个对象,那就大错特错了……对吗?

class Contact
  attr_accessor :auth_token

  def initialize(contact_hash)
     ...
  end

  def edit(...)
     auth_token.can! :read, self
  end
end

token = AuthorizationToken.new(session)
contact = SomeService.get_contact(...)

contact.edit(...)
# raise error because auth_token is not set

contact.auth_token = token

contact.edit(...)

上面的代码代表了我目前的困境:我希望 SomeService 给我 Contact 对象,但我不希望该服务根本不关心现有会话或授权。

我目前的方法是添加这个额外的 class:

class QueryService
  def initialize(session)
    token = AuthorizationToken(session)
  end

  def get_contact
    contact = SomeService.get_contact(...)
    contact.token = token
  end
end

contact = QueryService.new(session).get_contact(...)
contact.edit(...)

这个解决方案让我可以最自由地在核心域对象 Contact 中使用授权问题,在外部 class AuthorizationToken[ 中实现它们=30=] 并实现与当前用户会话无关的服务 SomeService

然而,两步构建让我很痛苦。感觉很奇怪:一个对象没有为某些操作完全初始化???

这不是依赖注入的普通情况,更确切地说是上下文注入。所以Ruby中大部分关于避免DI的文章并没有真正解决我的问题。我想知道是否有 more Ruby 方法 来解决这个问题,或者这是尽可能干净的方法。

看起来您的 Contact class 有两个目的 - 存储联系人数据和执行一些授权请求 - 所以是的,它确实违反了 Single Responsibility Principle

这可以通过将 Contact class 分成两部分来解决 - 一个可能是 Struct 甚至是普通哈希来存储数据,第二个可能是请求。

而且我认为最 Ruby 的方法是 return 来自 SomeService 的散列并用 [=14 实例化=]稍后。