2014-05-13 155 views
1

假設我們使用的是Rails或者只是ActiveSupport::Dependencies模塊。現在,讓我們來看看這個代碼:子類的循環依賴

animal.rb

class Animal 
    CHILD = { 
    cat: Cat 
    } 

    # factory!? 
    def self.child(name) 
    CHILD[name].new 
    end 
end 

cat.rb

class Cat < Animal 
end 

dog.rb

class Dog < Animal 
end 

所有的類加載某處之前:

Cat.new 

該代碼將導致B is not a class (TypeError)(activesupport 3.x)或Circular dependency detected while autoloading constant B(activesupport 4.x),因爲它尚未創建,但該類的名稱已在類表中。

要解決此問題,可能需要require 'a',然後A需要B

可以給我一些更好的解決方案,以解決以下問題嗎?

+0

你能更好地瞭解你打算做什麼嗎?總的來說,我可以想到爲什麼一個班級需要了解自己的子類的幾個原因。 –

+0

循環引用是麻煩的,除非你有一個特定的用例需要它。通常情況下,問題可以通過其他方式解決 - 您需要什麼樣的循環依賴? –

+0

@dsatch,我正在設置在子類中映射哈希CONSTANT和子類。作爲一種解決方案,我可以不斷地使用它的方法,但我認爲這不是一個正確的方法。 –

回答

2

如果你不想/不能使用require,你應該避免循環依賴。以避免它們被替換類加載水平的一個方法調用與運行水平的:

class Animal 
    CHILD = { 
    cat: 'Cat' 
    } 

    def self.child(name) 
    CHILD[name].constantize.new 
    end 
end 

事實上self.child不能在一流水平,現在叫,否則你將在一個循環依賴上下文一次。

+0

看起來不錯,我覺得這是唯一可以改善的地方。 –

+0

我也這麼認爲。好工作 – mdesantis

2

因此,爲了避免在Rails初始化或加載特定類時自動加載大量其他類,Rails在需要指定時使用字符串來指定關聯的類名,例如,

has_many :published_posts, class_name: 'Post' 

因爲關聯具有這樣的交互的最大傾向。

因此,如果你能控制你使用的內容,並且可以延遲常量的加載,直到需要在Ruby/Rails中使用字符串來保存常量的名稱,直到你需要對它進行常量化爲止,它可能不會是一個壞主意。但是,像任何事情一樣,最好的解決方案取決於您的要求。

如果您來自Java等其他編程語言背景,至少在開發人員會使用反射來動態地實例化字符串中的類(由於開銷較高)(至少在舊版本中)以及在以後加載類時與在啓動時儘早捕獲它們的錯誤的可能性。但是,這裏真的不是一件壞事。而且這也不會阻止你在預先加載時儘可能多地進行熱切加載,因爲實際上它正在消除加載期間由持續引用引起的直接依賴性。

+0

太好了,但在我的例子中,我使用的是ActiveAdmin,即在通過路由初始化之後初始化模型。我已經將我的示例更新爲更實際的用例。 –

+0

謝謝!是的,因爲你改變了這個問題,我的回答看起來不正確,但會保持原樣。基本上,這個想法是爲了避免循環依賴,你可以將常量存儲爲一個字符串,並在類加載後的某個時間進行常量化。你可能甚至想要在常量化之後緩存/存儲常量,如果它使事情更有效。 –