2011-07-05 14 views
6

Mam serię obiektów Ruby, które modelują podstawowy kod XML (jak OXM). Niestety, XML jest zmieniany, a odpowiednia wersja jest atakowana. Muszę zaktualizować moje obiekty Ruby, aby móc obsłużyć obie wersje. Chciałbym czegoś czystszego niż tona klauzuli if/else w moich metodach, ponieważ prawdopodobnie to się powtórzy. Czy istnieje idiomatyczny sposób na radzenie sobie z tym Ruby? Myślałem o użyciu klasy bazowej jako serwer proxy dla różnych „wersjonowanym” klas, tjWersja obiektu 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 

Zaletą powyższej metody jest to, że każda wersja różni się w kodzie, i pozwalając mi Add/Drop wersje z niewielkimi testami zgodności wstecznej. Najgorsze jest to, że prawdopodobnie będę miał dużo duplikacji kodu. Również w tym przypadku XMLModel.new zwraca nową XMLModel, podczas gdy metoda fabryczna XMLModel.from_xml zwraca nową XMLModel::V1.

Pomysły?

Odpowiedz

2

Dlaczego nie zbudować podklasy, która dziedziczy po XMLModel, wtedy decyzja pomiędzy klasami znajduje się w jednym punkcie kodu.

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

można ukryć, że 'if' oświadczenie w inicjalizacji XMLModel (tj' new') ... I nie może wydawać się bez użycia oddzielnego intialization fabryki, czyli 'XMLModel.create'. Chciałbym, żeby ludzie nie musieli wiedzieć o wersjach i mogli używać obiektu jako normalnego obiektu Ruby i wywoływać 'XMLModel.new', ukrywając w ten sposób całą implementację za modelem. –

+0

możesz zastąpić ':: new'. Domyślną nową metodą jest (myślę) coś w stylu: "def new (* args); obj = przydziel; obj.initialize (* args); retrun obj; end' Przepraszamy za jeden wiersz, komentarze i kod nie mieszają się dobrze. – Ian

+0

dodano odpowiedź z bardziej szczegółowym przedefiniowaniem :: nowy – Ian

3

Widzę kilka opcji dla ciebie.

Można wykonywać połączenia proxy na numer XMLModel z method_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 

Innym rozwiązaniem byłoby dynamicznie skopiować metody od konkretnej wersji do instancji XMLModel. Odradzałbym jednak robienie tego, chyba że byłoby to całkowicie konieczne.

Trzecią opcją jest utworzenie modułu dla każdej wersji, która ma metody właściwe dla tej wersji. Następnie, zamiast mieć obiekt proxy, wystarczy dołączyć moduł dla konkretnej wersji.

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

To będzie działać podczas ładowania XML, ale nie zadziała przy próbie utworzenia XML. Czy myślisz, że powinienem również użyć do tego metody innej niż "nowa" (np. "Create")? –

+0

Czy metoda 'to_xml' nie byłaby częścią żadnej z określonych klas wersji? – Jeremy

+0

Tak, to_xml byłby częścią każdej klasy wersji, ale jeśli użytkownik chce utworzyć dokument XML od podstaw, musi uzyskać nowy (pusty) obiekt, który następnie może "wypełnić", wywołując metody setera, sortowania. jak Rails ActiveRecord. –

2

To nie jest pełna odpowiedź, ale po prostu lepiej sformatowane wersja mój komentarz opisujący w jaki sposób można zastąpić :: nowy.

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 
Powiązane problemy