2011-07-05 39 views
6

我有一系列的Ruby對象,它們爲底層XML(比如OXM)建模。不幸的是,XML正在改變,相應的版本正在被碰撞。我需要更新我的Ruby對象,以便能夠處理這兩個版本。我想要的東西比我的方法中的一大堆if/else子句更清潔,因爲這可能會再次發生。有沒有一種慣用的Ruby方法來處理這個問題?我想用的基類的「版本」類的各種代理,即版本化Ruby對象

class XMLModel 
    class V1 
    # V1 specific implementation 
    end 

    class V2; 
    # V2 specific implementation 
    end 

    def initialize 
    # create a new V? and set up delegation to that specific version of the object 
    end 

    def from_xml(xml_string) 
    # use the XML string to determine correct version and return a 
    # specific version of the object 
    end 
end 

關於上述方法的好處是,每個版本的代碼中的不同,並允許我添加/刪除幾乎沒有向後兼容性測試版本。不好的一面是,我很可能會結束很多代碼重複。此外,在這種情況下,XMLModel.new會返回新的XMLModel,而XMLModel.from_xml工廠方法會返回新的XMLModel::V1

想法?

回答

2

爲什麼不建立繼承自XMLModel的子類,那麼類之間的決定只是在代碼中的一個點上。

class XMLModel_V1 < XMLModel 
    def from_xml(xml_string) 
     # do V1 specific things 
    end 
end 

class XMLModel_V2 < XMLModel 
    def from_xml(xml_string) 
     # do V2 specific things 
    end 
end 


# Sample code wich shows the usage of the classes 
if(V1Needed) 
    m = XMLModel_V1 
else 
    m = XMLModel_V2 
end 
+0

你可以在XMLModel初始化中隱藏'if'語句(即'new')......我似乎沒有使用單獨的工廠初始化,即'XMLModel.create'。我希望人們不必知道版本控制,並能夠將該對象用作普通的Ruby對象,並調用XMLModel.new,從而隱藏模型後面的所有實現。 –

+0

你可以重寫':: new'。默認的新方法是(我認爲)是這樣的:'def new(* args); obj = allocate; obj.initialize(*參數);逆時針結尾'抱歉的一行,評論和代碼混合不好。 – Ian

+0

添加了一個更詳細的重新定義答案:: new – Ian

3

我看到了幾個選項。

您可以通過XMLModelmethod_missing代理方法調用。

class XMLModel 

    def load_xml(xml) 
    version = determine_version(xml) 
    case version 
    when :v1 
     @model = XMLModelV1.new 
    when :v2 
     @model = XMLModelV2.new 
    end 
    end 

    def method_missing(sym, *args, &block) 
    @model.send(sym, *args, &block) 
    end 

end 

另一種選擇是動態的方法,由一個特定的版本複製到XMLModel實例。但是,除非完全有必要,否則我會勸阻這樣做。

第三個選項是爲每個版本創建一個模塊,該版本具有特定於該版本的方法。然後,而不是有一個代理對象,你將只包含特定版本的模塊。

module XMLModelV1 
    #methods specific to v1 
end 

module XMLModelV2 
    #methods specific to v2 
end 

class XMLModel 
    #methods common to both versions 

    def initialize(version) 
    load_module(version) 
    end 

    def load_xml(xml) 
    load_module(determine_version(xml)) 
    end 

private 

    def load_module(version) 
    case version 
    when :v1 
     include XMLMOdelV1 
    when :v2 
     include XMLModelV2 
    end 
    end 
end 
+0

這將在加載XML時起作用,但在嘗試創建XML時不起作用。你是否認爲我應該使用非'new'工廠方法(即'create')? –

+0

'to_xml'方法是否是特定版本類的一部分? – Jeremy

+0

是的,to_xml是每個版本化類的一部分,但是如果用戶想從頭開始創建一個XML文檔,他們需要獲得一個新的(空的)對象,然後通過調用setter方法來「填充」像Rails ActiveRecord。 –

2

這不是一個完整的答案,而只是我的評論的一個更好的格式化版本,描述如何覆蓋:: new。

class XMLModel 
    def new *args 
    if self == XMLModel 
     klass = use_v1? ? XMLModelV1 : XMLModelV2 
     instance = klass.allocate 
     instance.initialize *args 
     instance   
    else 
     super *args 
    end 
    end 
end 
# and of course: 
class XMLModelV1 < XMLModel; end 
class XMLModelV2 < XMLModel; end