版本化Ruby对象

时间:2011-07-05 14:50:32

标签: ruby versioning idioms

我有一系列基础XML的Ruby对象(如OXM)。不幸的是,XML正在被更改,相应的版本正在受到冲击。我需要更新我的Ruby对象才能处理这两个版本。在我的方法中,我想要比大量的if / else子句更清洁,因为这可能会再次发生。是否有一种惯用的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

想法?

3 个答案:

答案 0 :(得分:3)

我为你看了几个选项。

您可以使用XMLModel代理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

另一种选择是将方法从特定版本动态复制到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

答案 1 :(得分: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

答案 2 :(得分: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