2015-10-13 94 views
2

我已經建立了兩個類如下圖所示紅寶石繼承和重寫類方法

class Parent 

    def self.inherited(child) 
    child.custom_class_method 
    end 

    def self.custom_class_method 
    raise "You haven't implemented me yet!" 
    end 
end 

class Child < Parent 

    def self.custom_class_method 
    "hello world" 
    end 
end 

看來,當的傳承Child < Parent評估,它調用self.inherited,這又引發了self.custom_class_methodParent的版本而不是Child的。這是因爲,而不是讓問題預期"hello world"我得到一個錯誤募說"You haven't implemented me yet!"

Child的,直到後self.custom_class_method沒有得到評估Parentself.inherited完成評估?如果是的話,那麼可能是圍繞這個做了一個工作?我是不是應該在父類上放一個raise檢查?

+0

這似乎很奇怪。獲得父類的'custom_class_method'的唯一方法應該是調用'super'。否則,只調用'Child.custom_class_method'應該會導致您的「hello world」輸出。你能否提供更深入的日誌記錄? –

+0

嗯,我同意它應該!但似乎我可以通過將代碼塊複製粘貼到「irb」控制檯來複制此內容。由於這個原因,評估「Child」類時會出錯。 – aMat

回答

3

我認爲這應該澄清:

class Parent 
    def self.inherited(child) 
    puts "Inherited" 
    end 
end 

class Child < Parent 
    puts "Starting to define methods" 
    def self.stuff; end 
end 

輸出清楚地表明,.inherited被稱爲打開新類的那一刻,而不是當你關閉它。因此,正如你猜測的那樣,Child.custom_class_method在你試圖調用它的時候不存在 - 所有的.inherited都是空白的。

(至於怎麼去解決它......我不能沒有更深入到說你正在嘗試做的,很遺憾。)

+0

啊聰明,我沒有想到這樣調試它!那麼在這裏定義抽象方法的最佳方式是什麼?我只是避免提出錯誤(這是我不坦率的說實話)。 – aMat

+0

基本上我想要做的是創建一個能夠在'self.inherited'塊中執行代碼的父類,它將從子類到子類稍有不同。然而,爲了確保我們不會出錯,我想強制每個繼承孩子都會定義'custom_method'(因此爲什麼我會提升Parent類)。 – aMat

+0

不要問「我如何獲得繼承的子代碼」,因爲你不能。這似乎是一個XY問題。 *爲什麼*你想要繼承孩子代碼? '.inherited'是爲了通知新生兒的父母,而不是在兒童身上運行密碼。 *孩子*可以在孩子身上運行代碼(參見我的'puts')。 – Amadan

2

模板模式/延遲初始化可能會幫助您解決問題。代碼假設子類之間有什麼不同是數據庫連接信息,也許只是一個表名或可能完全不同的數據庫。父類擁有創建和維護連接的所有代碼,只讓兒童承擔提供不同的責任。

class Parent 
    def connection 
    @connection ||= make_connection 
    end 

    def make_connection 
    puts "code for making connection to database #{database_connection_info}" 
    return :the_connection 
    end 

    def database_connection_info 
    raise "subclass responsibility" 
    end 
end 

class Child1 < Parent 
    def database_connection_info 
    {host: '1'} 
    end 
end 

class Child2 < Parent 
    def database_connection_info 
    {host: '2'} 
    end 
end 

child1 = Child1.new 
child1.connection # => makes the connection with host: 1 
child1.connection # => uses existing connection 

Child2.new.connection # => makes connection with host: 2 
+0

真棒的東西,謝謝!作爲一個設計問題,你認爲這比將數據抽象成一個DatabaseManager模型更好/更糟糕,該模型維護這個連接邏輯並在每個類中調用DatabaseManager.make_connection而不是繼承它? – aMat

+0

優秀的問題。代表團與繼承是一個古老的問題。 Rails通過繼承進行持久化(技術上可以包含模塊,但通常通過繼承來完成)。許多人認爲持久性應該不是班級的責任,而是走向另一個方向,比如數據映射模式(https://en.wikipedia.org/wiki/Data_mapper_pattern)。即使你屬於持久性責任陣營,你仍然可能想在課堂外抽象出數據庫行爲 - 可能是連接池。這裏有很好的庫,比如續集寶石。 –

+0

我沒有結合'DatabaseManager'的想法,所以這是另一個很好的建議。正如我所說,我的答案一定是模糊的,因爲你比我更瞭解你的項目方式。我試圖做的主要觀點是,你不能在'繼承'上做到這一點。你可以使用外部管理器,或者像這裏一樣使用繼承方法,或者甚至在祖先上使用'initialize';但「繼承」有另一個目的。 – Amadan