Ruby 两步施工。有用还是只是一种代码气味?

Ruby 两步施工。有用还是只是一种代码气味?,ruby,context-injection,Ruby,Context Injection,通常情况下,我希望一旦构建了一个对象,它就应该准备好使用了。没有两步施工。如果你需要调用两个构造函数来使用一个对象,那就大错特错了。。。对吧? class Contact attr_accessor :auth_token def initialize(contact_hash) ... end def edit(...) auth_token.can! :read, self end end token = AuthorizationToken.n

通常情况下,我希望一旦构建了一个对象,它就应该准备好使用了。没有两步施工。如果你需要调用两个构造函数来使用一个对象,那就大错特错了。。。对吧?

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 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内使用授权关注点,在外部类AuthorizationToken中实现它们,并实现与当前用户会话SomeService无关的服务

然而,这两步施工让我非常痛苦。感觉很奇怪:对于某些操作,对象没有完全初始化


这不是依赖注入的简单情况,更确切地说是上下文注入。所以大多数关于在Ruby中避免DI的文章并没有真正解决我的问题。我想知道是否有一种更具Ruby风格的方法来解决这个问题,或者这是最干净的方法。

看起来你的
Contact
类有两个目的——存储联系人数据和执行一些授权请求——所以是的,它确实违反了规则

这可以通过将
Contact
类拆分为两个来解决-一个可以是
Struct
或者甚至是一个普通的散列来存储数据,第二个完成请求


我认为最适合Ruby的方法是从
SomeService
返回哈希值,稍后用
Contact.new(数据、身份验证令牌)
实例化。

这看起来确实有点奇怪。另外,您是否曾从
QueryService
调用
get\u contact
?我刚刚在auth token的调用中添加了
self
,以显示我使用ioc来减轻授权逻辑的负担。实际上,
Contact
不是一个普通的存储模型,而是一个域聚合根。我不想提及DDD试图让我的问题更简单。我更想知道使用两步初始化是否是一种代码味道。经过一些实验后,我很快注意到我的示例中的授权对象是一个完全疯狂的想法,显然正走向麻烦。这是一个非常糟糕的例子!尽管这个答案并没有直接回答标题中的问题,但两阶段的构造直接导致SRP的中断,因为在第二次初始化之前和之后,对象可以做什么有一个转换。SRP的明确突破似乎表明两阶段构造实际上是一种代码smell@SystematicFrank哦好的,从DDD的角度来看,服务肯定应该返回值对象而不是实体,这应该将VO从对令牌的依赖中解放出来。