Francis Hwang
2/13/2006 8:05:00 PM
Personally, I'd be skeptical of the value of an intermediate canonical
form, though I suppose this might depend on how different the feeds are
from one another. But generally, I think Ruby offers lots of
opportunities for factoring into common utility code, enough that an
intermediate format is arguably less useful in Ruby than in a more
verbose language.
If I were in your shoes, I think I'd make separate feed classes, backed
by lots of tests, and then look aggressively for opportunities to
refactor into common methods. In Ruby, factoring can take lots of
inventive forms, such as custom iterators like #each_product. And don't
forget there's lots of chances for reflectivity. For example:
class AbstractFeed
def format_state
state.send "to_#{ state_format }"
end
end
class FeedA < AbstractFeed
def state_format; "fullname"; end
end
class FeedB < AbstractFeed
def state_format; "postal_code"; end
end
Maybe you could even define yourself a mini-feed DSL, so that classes
could look like:
class FeedA < AbstractFeed
formats :state => :fullname, :zipcode => :five_digits
end
class FeedB < AbstractFeed
formats :state => :postal_code, :zipcode => :nine_digits
customers_select { |c| c.orders.size >= 1 }
orders_select { |o| o.amount >= 100.0 }
end
.... though this may also be more or less useful depending on how widely
the feeds vary.
eastcoastcoder@gmail.com wrote:
> I'm trying to master the more subtle elements of Rubyish OO design, and
> am having trouble with the following aspect:
>
> I have a complicated, nested, value object ( eg
> order.customer.address.zipcode, or order.product[0].description).
>
> This object needs to be sent to various feeds (usually XML, sometimes
> REST / HTTP form ) run by other companies. Each company has a slightly
> different format in which they want some of the items (eg, some might
> want CA and some California, some might want the description to use one
> set of abbreviations and some another).
>
> Now, the feeds are added fairly frequently, and so a goal of the design
> is to allow new feeds to be created without having to modify the core
> code.
>
> Questions:
>
> 1. Which object should be responsible for formatting a field according
> to one spec's choice? Should it be
> product.format_description_for(feedX), or
> feedX.format_description(product) ? "Tell, don't ask" would indicate
> the first. The product knows better than anyone how to describe
> itself. But there are two arguments for the latter: A) New feeds can
> be added without touching the product code and B) Feeds know which type
> of formats they need - very often, different feeds could use the same
> formats for many fields.
>
> It seems that the root of the problem is that OO design tells us to put
> logic with the data it operates on, and to push decisions out to that
> thing. But here, the question can be answered only by two things
> working together: the order (which knows about itself) and the feed
> (which knows what type of formatting it needs).
>
> Ideas?
>
> 2. What type of method call should I use to format the fields? It can
> get cumbersome to always add a formatter, even when it's not needed (eg
> I'd rather just pass order.customer.address.zipcode, and have the to_s
> called implicitly, than have to always write
> order.customer.address.zipcode.format_for(feedX) - when zipcodes are
> almost always formatted the same). The code could be made more succint
> by just adding an instance variable @formatter, and having to_s
> automatically use that. But there are two problems with that A) How
> would subobjects find the @formatter? In other words, we can set
> order.formatter, but how will order.customer.address find the
> formatter? and B) This violates encapsulation - if, in the middle, we
> need a field in a different format, we can mess things up without
> realizing.
>
> So: Is there a good solution to avoid having the repetition of
> constantly appending .format_for(feedX)?
>
> All ideas/comments/criticisms/creativity appreciated
>
> PS Yes, I know that a nested value object arguably violates demeter.
> PPS If it's relevant, the XML is generated using ERB templates.