【问题标题】:How do I require classes with a circular dependency? [duplicate]我如何要求具有循环依赖的类? [复制]
【发布时间】:2013-04-01 00:25:00
【问题描述】:

Rail 自动加载丢失的常量让我很受宠若惊。在 Ruby 中,如果我有两个类,一个嵌套在另一个内部但在不同的文件中,我如何要求它们,因为它们相互依赖(循环依赖)。

# user.rb
class User < ActiveRecord::Base
   serialize :preferences, User::Preferences
end

# user/preferences.rb
class User::Preferences
end

# user_spec.rb    
require 'user'
require 'user/preferences'

注意:我需要 Rails 环境。

如果我先尝试加载用户,代码会失败,因为它还不知道User::Preferences。如果我先加载“用户/首选项”,它会在加载用户时失败,因为现有的用户类不是 ActiveRecord 的子类。

我怀疑我需要删除循环依赖,或者,如果可能的话,通过传递一个字符串 'User::Preferences' 来使 serialize 延迟加载类,该字符串在需要时转换为常量。

【问题讨论】:

  • 这不是重复的,它有相同的主题,但不同的问题。在“重复”中,依赖项位于方法调用中,而不是像我的示例代码那样的类主体。在“重复”中实际上没有问题,请注意答案:“Ruby 不是 C++,它不会抱怨 FooSub.SOME_CONSTANT 直到您调用 Foo#foo() ;)”。对我来说这是不正确的,因为我的依赖项在类主体中,一旦加载类就会调用它,而不是在加载依赖项后的某个未来点。

标签: ruby ruby-on-rails-3


【解决方案1】:

我的一个技巧是在 user/preferences.rb 中创建一个空的 User 类,继承自 ActiveRecord::Base:

class User < ActiveRecord::Base; end

class User::Preferences
end

【讨论】:

  • 我不认为这是“黑客”。您可以随时打开课程这一事实是 Ruby 的最佳特性之一。你在这里说,“好的,我在此断言有一个用户类”;如果这个 User 类是已知的,则断言没有害处,如果不是,它会导致该类是已知的。
  • 这听起来像是对糟糕的类文件设计的下意识反应。我会做类似@dbenhur 的建议的事情。创建其他两个文件需要的存根。
  • @theTinMan 我不认为是类,而是跨多个文件的信息分布导致了 OP 问题。这可能有充分的理由。
  • @matt 我认为这是一个 hack,因为我只是将空类用于依赖加载工作。
  • @theTinMan 如果 Prefrences 嵌套在 User 的同一个文件中,你还会认为它有异味吗?
【解决方案2】:

您可以将存根声明放在一个公共基础中,而不是将User 的实现知识连接到User::Preferences,如下所示:

# user_base.rb
class User < ActiveRecord::Base; end

# user.rb
require 'user_base'
require 'user/preferences'

class User
   serialize :preferences, User::Preferences
   ...
end

# user/preferences.rb
require 'user_base'

class User::Preferences
end

或者,您可以将User::Preferences 移动到独立的模块命名空间中,例如ModelHelper::User::Preferences。我想我更喜欢这个解决方案。您具有循环依赖的事实是代码异味,唯一导致它的原因是User 类作为User::Preferences 的命名空间容器的重用。

【讨论】:

  • +1 “你有循环依赖的事实是代码异味”。同意。
  • base_user.rb 看起来与我发布的“hack”相同,即创建一个具有正确继承的空类并稍后重新打开它。我不喜欢添加 ModelHelper 命名空间的想法。 Preferences 不是助手,它是User 特有的值对象,所以我的想法是,它应该嵌套在User 中。我确实感觉这可能是一种气味,但是因为没有明显的解决方案。
  • 在 Ruby 中,模块命名空间本质上是任意的。就Bars 与Foo 的关系而言,Foo::BarBarBaz::Bar 相比没有语义意义。 Bar 不是 Foo 私有的,也没有对 Foo 的任何特殊隐式或显式访问,反之亦然。但是,通过在Foo 中定义Bar,您引入了一个额外的要求,即定义Foo,这要求Bar 知道有关Foo 的细节才能正确地做到这一点(类或模块?什么超类),因为它不能只是@987654342 @当FooBar有真正的语义依赖时。
  • 前向声明确实是与您的“hack”类似,但考虑到您安排的依赖关系,它也是一个要求。把它移到一个共同的基地使它稍微不那么干燥,而且更干燥。现在只有一个地方可以说明User 是类还是模块,以及它的超类可能是什么。
猜你喜欢
  • 2013-12-10
  • 2015-01-04
  • 1970-01-01
  • 2020-06-24
  • 2021-08-15
  • 2019-05-24
  • 2011-12-24
  • 1970-01-01
  • 2018-07-17
相关资源
最近更新 更多