

|
Valid XHTML v1.0
Author Profile
Top Authors
Log In to set widget preferences
| Author | # |
|---|---|
|
185 |
|
148 |
Anonymous User
|
49 |
|
28 |
Kimberly Hieber
|
27 |
|
23 |
Note: some conversations may be proxied or secured, thereby causing # differences
Subscribe
Partners
Recent
Tags
Log In to set widget preferences
Recent
Log In to set widget preferences
Most Active
Log In to set widget preferences
Popular
Log In to set widget preferences
|
|
Factory Pattern, Ruby Style
belongs to Coding Tips and Tricks ![]() by terry on 2008-08-07 07:40 PM read 219 times |
In Java and .NET, when I'd write factory methods, it was somewhat handy because I could conceal all the logic for determining class type in one method. Here's something like a typical factory method in Java (ganked from Wikipedia):
public class ImageReaderFactory
{
public static ImageReader getImageReader( InputStream is )
{
int imageType = figureOutImageType( is );
switch( imageType )
{
case ImageReaderFactory.GIF:
return new GifReader( is );
case ImageReaderFactory.JPEG:
return new JpegReader( is );
// etc.
}
}
}
This means that the other classes who care about the reading an image, but not how to read it, don't have to know anything about that. They just say, "hey, here's my input stream. Can you read it for me?"
For the last iteration, doing some Facebook integration, I ran into a similar situation. There's lots of types of Facebook elements - Affiliation XML stanzas, single line birthdays, pictures, etc., and they all have different ways that they need to render to html. It seemed like a prime candidate for a factory method:
def self.create(title, body)
element = nil
if title =~ /pic/
element = ImageFacebookElement.new(title, body)
elsif title == "affiliation"
element = AffiliationFacebookElement.new(title, body)
...
element
end
Unfortunately, this felt incredibly dirty. Especially in Ruby. It sucks that any time Facebook introduces a new type of object, I have to edit the base code. I know Ruby's got open classes and all, but I don't really want to be screwing with that method unnecessarily. So, instead of doing that, I extracted the FacebookElement identification tests to their respective rendering classes, and introduced a means to register an element with the factory. First, here's what the factory looks like now:
class FacebookElement
@@title_tests = []
@@body_tests = []
def self.register(klass, type)
@@title_tests << klass if type == :title
@@body_tests << klass if type == :body
end
def self.create(title, body)
@@title_tests.each do |t|
return t.new(title, body) if t.matches(title)
end
@@body_tests.each do |t|
return t.new(title, body) if t.matches(body)
end
FacebookElement.new(title, body)
end
end
I only allow for two types of categorization for tests: title and body. In my case, title tests seemed to be more exact, so I apply those criteria first. If I needed more precision, I could add some other heuristic or attribute to keep up with what tests to run when. The great thing about this approach is that FacebookElement never has to change.
Now, let's look at one of the more concrete implementations of a FacebookElement:
class ImageFacebookElement < FacebookElement
FacebookElement.register(self, :body)
def self.matches(body)
body =~ /(png|jpg|jpeg|gif)$/
end
def to_html
#magic image facebook element rendering code
end
end
I used ImageFacebookElement because it was actually the last class I had to add, and it was ridiculously easy. Find a new facebook element, find a distinguishing characteristic and how it's displayed, code that display method into to_html, code the distinguishing characteristic into the self.matches section, and register with the FacebookElement class.
If you're curious how the rest of that code works, just look at the kalivo codebase in vendor/plugins/facebook_profile. It's actually pretty simple, thanks to how easy Ruby makes lots of patterns.
Log In to Reply |
Log In to Copy |
Tell a Friend
|
Trackback URL: http://www.kalivo.com/trackback/1566-factory-pattern-ruby-style
