From: Florian Gross Date: 2004-11-13T18:23:25+09:00 Subject: Re: advice for a class implementation? ( ugh, long... ) Corey wrote: > On Thursday 11 November 2004 03:13 pm, Florian Gross wrote: >>I would expect the problem space to get a lot simpler if you'd >>decide to use a prevalence system instead. > I'm not quite sure what you mean by prevalence system. http://madeleine.sourceforge.net/ is a good example of one. > Though the various backends/datastores ( QuoteCollection::#{persistence} ) > won't share any actual implementation - I _do_ want to ensure that they will > all share the same _interface_... I want them all to have the same public > methods ( though, of course, not necessarily the same private methods or > the same public/private attributes ) so that from the user's perspective, it > will appear that they in fact do have the same functionality. More about this > further down below. You can use test cases for this: require 'test/unit' class QuoteCollectionTest < Test::Unit::TestCase def initialize(qc, *more) @qc = qc super(*more) end def run(result) if self.class != QuoteCollecionTest then super end end def test_store_and_fetch_all quote = Quote.new(:author => "Foo", ...) @qc.store(quote) assert_equal([quote], @gc.fetch_all) end ... end class QuoteCollectionYAMLTest < QuoteCollectionTest def initialize(*more) super(QuoteCollection::YAML.new("filename"), *more) end end and so on. > After pondering over all this, I realized that I was definitely missing > something that would allow all these pieces to work together. Turns > out there were two things. > > [simplify QuoteCollection and Quote classes] > So what I need now is a "QuoteMachine" class... it provides the > necessary interface between Quotes and a QuoteCollection. It will > be through the instantiation of a QuoteMachine by which the means > of normalized interaction/access to any particular QuoteCollection > will be performed. This will be how the user searches Quotes, and > displays Quotes, removes/adds/modifies Quotes, etc - the > QuoteCollection backend and the Quote class itself will be totally > transparent... QuoteMachine will have .from_collection and .to_collection > private methods that will fullfill the roll of translating a Quote object > in/out from a QuoteCollection::#{persistence} data format. But this isn't compatible with the want of an optimized SQL adapter. You couldn't use SQL for searching through quotes for example. I guess the best way would be to let each QuoteCollection class implement the bare minimum of functionality you described above and add everything else around that as methods to your QuoteCollection::Base class. This would make it easy to add a new adapter (for example for YAML) while still allowing to optimize specific cases that can be implemented faster than by using the primitive methods. (like searching for specific quotes) class QuoteCollection::Base def find_by_author(author) fetch_all.find_all do |quote| quote.author == author end end end class QuoteCollection::YAML < QuoteCollection::Base def initialize(filename) @quotes = YAML.load(File.read(filename)) end def fetch_all @quotes end end class QuoteCollection::SQL < QuoteCollection::Base def initialize(backend) @backend = backend end def fetch_all @backend.execute("SELECT ALL FROM quotes") end # Overridden, more efficient implementation def find_by_author(author) @backend.execute("SELECT ALL FROM quotes WHERE author = '%s'", author) end end This all being heavily simplified of course.