2012-06-17 89 views
9

給定一個類層次結構如下:如何在初始化父類時返回子類的新實例?

class A 
    def initialize(param) 
    if param == 1 then 
     #initialize and return instance of B 
    else 
     #initialize and return instance of C 
    end 
    end 
end 

class B < A 
end 

class C < A 
end 

是否有可能實際初始化並返回BC實例初始化A什麼時候?即my_obj = A.new(param)會導致my_objBC的實例,具體取決於param的值,該值將在A.initialize(param)中進行檢查。

在我的用例中,它在運行時只知道使用哪個子類(BC)和父類(A)基本上從未真正使用過。 我認爲將決定B還是C的邏輯轉移到它們的共同祖先中可能是一個好主意。

如果這是不可能的(或不好的風格),我應該在哪裏應該檢查param以及決定哪個類初始化?

+1

以前回答過類似的問題。你可能在尋找的是工廠方法。看看http://stackoverflow.com/questions/1515577/factory-methods-in-ruby我想在http://stackoverflow.com/a/1515580/1128705答案將符合您的需求。 –

回答

8

你在這裏打破了基本原則,面向對象 - 類應一無所知他們子類。當然,有時候應該打破原則,但在這裏沒有明顯的理由。

更好的解決方案是將實例化邏輯轉移到單獨類中的工廠方法。工廠方法採用與上述A的初始化器相同的參數,並返回相應類的實例。

+0

謝謝你大聲說出來。 :)我終於在一個單獨的模塊中編寫了一個'initiate_A'方法,它做出決定並返回一個新的對象。 –

+0

雖然這可能有一個或兩個情況適用。正如我在我的回答中所說的,如果你有一個解析器類,例如不同版本的語言必須處理?你可以有一個構造函數返回正確版本的數據的正確子類的實例。 HTML5是HTML不是一個很好的繼承關係,我不知道是什麼。 – Linuxios

+0

我們同意彼此 - 當有充分的理由時,這個原則可以被打破。不知道我喜歡你的例子,這聽起來更像是抽象工廠的案例。 – ComDubh

3

事情是,initialize的返回值被忽略。這裏是當你調用A.new會發生什麼:

  • new調用名爲allocate一類特殊的方法 - 它返回類的空實例
  • new然後調用initializeallocate返回的對象,並返回對象

做你想做的事,你需要重寫new,使其做你想做的:

class A 
    def self.new(args*) 
    if(args[0]==1) 
     B.new(*args[1..-1]) 
    else 
     C.new(*args[1..-1]) 
    end 
    end 
end 

還有其他事情要考慮。如果A從來沒有真正使用它,你應該使用某種工廠方法,或者,只是一個簡單的if語句。例如:

def B_or_C(param,args) 
    (param == 1 ? B : C).new(args) 
end 

設計真的取決於您使用的是什麼。例如,如果您有一個可用於處理多個版本的類(例如HTML)的類,則可以使用主類HTMLParser,該類重寫new,並且可以返回其任何子類:HTML1Parser,HTML2Parser,HTML3Parser,HTML4Parser ,和HTML5Parser

注意:您必須重寫new方法在子類的默認,以防止無限循環:

def self.new(args*) 
    obj=allocate 
    obj.send(:initialize, *args) 
    obj 
end 
+0

謝謝你的回答。但是,當在'A'中重寫'self.new'並調用'A.new(params)'時,出現一個錯誤:'SystemStackError:stack level too deep'。看起來,在'A.new'內調用'B.new'(或'C.new')會導致無限循環,因爲B和C的「構造函數」以某種方式調用了「構造函數」 'A',即'A.new'。 –

+0

由於傳統 –

+0

@Torbjoern:請參閱編輯。 – Linuxios