2011-07-05 15 views
6

Tengo una serie de objetos Ruby que modelan XML subyacente (como un OXM). Lamentablemente, el XML se está modificando y la versión correspondiente se está ejecutando. Necesito actualizar mis objetos de Ruby para poder manejar ambas versiones. Me gustaría algo más limpio que una tonelada de cláusulas if/else en mis métodos, ya que es probable que esto vuelva a ocurrir. ¿Hay una forma idiomática de Ruby para manejar esto? Estaba pensando en el uso de la clase base como un proxy para varios "versionado" clases, es decirControl de versiones de un objeto 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 

Lo bueno del método anterior es que cada versión es distinta dentro del código, y lo que me permite de inserción/extracción versiones con poca prueba de compatibilidad hacia atrás. Lo malo es que probablemente terminaré con mucha duplicación de código. Además, en este caso, el XMLModel.new devuelve un nuevo XMLModel, mientras que el método de fábrica XMLModel.from_xml devuelve un nuevo XMLModel::V1.

Ideas?

Respuesta

2

Por qué no crear la subclase que hereda de XMLModel, entonces la decisión entre las clases es solo en un punto del código.

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

¿Se puede ocultar esa declaración 'if' en la inicialización XMLModel (es decir,' new') ... Me parece que no puede sin usar una inicialización de fábrica por separado, es decir, 'XMLModel.create'. Me gustaría que la gente no tenga que saber sobre el control de versiones y poder usar el objeto como un objeto Ruby normal y llamar a 'XMLModel.new', ocultando así toda la implementación detrás del modelo. –

+0

puede anular ':: new'. El nuevo método predeterminado es (creo) algo así como: 'def new (* args); obj = asignar; obj.initialize (* args); retrun obj; end' Disculpa por el texto único, los comentarios y el código no se mezclan bien. – Ian

+0

agregó una respuesta con más elaboración de la redefinición :: new – Ian

3

Veo algunas opciones para usted.

Puede realizar llamadas de método proxy en XMLModel con 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 

Otra opción sería copiar dinámicamente los métodos de una versión específica a la instancia de XMLModel. Sin embargo, desalentaría hacer esto a menos que sea completamente necesario.

La tercera opción es crear un módulo para cada versión que tenga los métodos específicos para esa versión. Entonces, en lugar de tener un objeto proxy, solo incluirá el módulo para la versión particular.

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

Eso funcionará al cargar XML, pero no funcionará cuando intente crear XML. ¿Estás pensando que debería utilizar un método de fábrica que no sea 'nuevo' para eso también (es decir,' crear')? –

+0

¿No sería el método 'to_xml' parte de cualquiera de las clases versionadas específicas? – Jeremy

+0

Sí, to_xml sería parte de cada clase versionada, pero si el usuario desea crear un documento XML desde cero, necesita obtener un nuevo objeto (vacío) que luego puede 'completar' llamando a métodos setter, por lo tanto, como Rails ActiveRecord. –

2

Esto no es una respuesta completa, pero sólo en una versión mejorada con formato de mi comentario que describe cómo se puede anular :: nueva.

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 
Cuestiones relacionadas