2011-07-05 28 views
6

Tôi có một loạt các đối tượng Ruby mô hình hóa XML cơ bản (như một OXM). Thật không may, XML đang được thay đổi và phiên bản tương ứng đang được bumped. Tôi cần cập nhật các đối tượng Ruby để có thể xử lý cả hai phiên bản. Tôi muốn một cái gì đó sạch hơn một tấn nếu/else mệnh đề trong phương pháp của tôi, vì điều này có khả năng xảy ra một lần nữa. Có một cách Ruby độc đáo để xử lý điều này? Tôi đã nghĩ đến việc sử dụng các lớp cơ sở như là một proxy cho nhiều "phiên bản" lớp học, tức làVersioning a Ruby Object

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 

Những điều tốt đẹp về các phương pháp trên là mỗi phiên bản là khác biệt trong mã, và cho phép tôi để thêm/thả các phiên bản có ít khả năng tương thích ngược. Điều xấu là tôi sẽ có khả năng kết thúc với rất nhiều bản sao mã. Ngoài ra, trong trường hợp này, số XMLModel.new trả lại XMLModel mới, trong khi phương thức nhà máy XMLModel.from_xml trả về một XMLModel::V1 mới.

Ý tưởng?

Trả lời

2

Tại sao không xây dựng lớp con kế thừa từ XMLModel, thì quyết định giữa các lớp chỉ là một điểm trong mã.

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

Bạn có thể ẩn câu lệnh 'if' đó trong khởi tạo XMLModel (tức là' mới') ... có vẻ như tôi không sử dụng tính năng intialization riêng biệt, tức là 'XMLModel.create'. Tôi muốn mọi người không phải biết về phiên bản và có thể sử dụng đối tượng như một đối tượng Ruby bình thường và gọi 'XMLModel.new', do đó ẩn tất cả việc triển khai đằng sau mô hình. –

+0

bạn có thể ghi đè lên ':: new'. Phương pháp mới mặc định là (tôi nghĩ) một cái gì đó như: 'def new (* args); obj = phân bổ; obj.initialize (* args); retrun obj; end' Xin lỗi cho một-lót, ý kiến ​​và mã không kết hợp tốt. – Ian

+0

thêm câu trả lời với việc xây dựng lại nhiều hơn nữa :: new – Ian

3

Tôi thấy một vài tùy chọn cho bạn.

Bạn có thể gọi phương thức proxy trên XMLModel với 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 

Một tùy chọn khác là sao chép động các phương thức từ một phiên bản cụ thể sang thể hiện XMLModel. Tuy nhiên, tôi sẽ không khuyến khích làm điều này trừ khi hoàn toàn cần thiết.

Tùy chọn thứ ba là tạo mô-đun cho mỗi phiên bản có phương pháp cụ thể cho phiên bản đó. Sau đó, thay vì có một đối tượng proxy bạn sẽ chỉ bao gồm các mô-đun cho phiên bản cụ thể.

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

Điều đó sẽ hoạt động khi tải XML, nhưng sẽ không hoạt động khi cố gắng tạo XML. Bạn có nghĩ rằng sau đó tôi nên sử dụng phương pháp nhà máy không phải '' mới '' (tức là 'tạo') ​​không? –

+0

Phương thức 'to_xml' có phải là một phần của một trong các lớp được phiên bản cụ thể không? – Jeremy

+0

Có, to_xml sẽ là một phần của mỗi lớp được phiên bản, nhưng nếu người dùng muốn tạo một tài liệu XML từ đầu, họ cần lấy một đối tượng mới (trống) mà họ có thể 'điền' bằng cách gọi phương thức setter, sorta- như Rails ActiveRecord. –

2

Đây không phải là câu trả lời đầy đủ, nhưng chỉ là phiên bản được định dạng tốt hơn của nhận xét mô tả cách bạn có thể ghi đè :: mới.

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