Singletons: Only The Lonely
I’ve never really spent much time on design patterns, but recently I’ve been reading Head First Design Patterns and I can’t help but notice how much easier Ruby makes certain patterns. Composition over inheritance becomes as easy as sneezing with mixins, and the thoughtful Ruby folks have already implemented quite a few.
I don’t use Singleton as much as I should (preferring instead to just stuff all the methods in the class), but for objects which have an expensive or complicated instantiation method, it’s extremely useful. (I mean, that’s why it’s a pattern, right?) Also, the Singleton mixin takes care of all the threading complications to which Singletons often fall prey.
But effective and useful it may be, Singleton is ugly, and for one big reason: instance. I hate typing instance all over the place, and it’s been a big stumbling block in my acceptance of the Singleton mixin. I understand why it’s there–the pattern doesn’t mention moving methods from instance to class scope–but on a syntactic level it irks me. If I’m using a singleton, I really shouldn’t need to know that it is a singleton–it could be a magic happy fairy princess castle for thread-safe methods, for all I care.
So yeah, I went and did something about it. I combined the power of Singleton and Proxy. Check it out:
module Singleton
class << self
def included_with_proxy(base)
included_without_proxy(base)
base.class_eval do
class << self
def method_missing_with_proxy(m, *args)
if instance.respond_to?(m)
instance.send(m, *args)
else
method_missing_without_proxy(m, *args)
end
end
alias_method_chain :method_missing, :proxy
def methods_with_proxy
return (methods_without_proxy + instance.methods).uniq
end
alias_method_chain :methods, :proxy
def respond_to_with_proxy?(m)
return respond_to_without_proxy?(m) || instance.respond_to?(m)
end
alias_method_chain :respond_to?, :proxy
end
end
end
alias_method_chain :included, :proxy
end
end
You’ll need to require singleton and activesupport for this one, and since it uses alias_method_chain, you’ll need to either be running Rails edge or grab it from the source.
What it does is pretty simple: if it’s not already a class method, Singleton passes it on to its instance:
class Magician
include Singleton
def dove
"*poof* A dove!"
end
def segway
"*rides Segway around*"
end
end
Magician.dove #=> "*poof* A dove!"
Magician.segway #=> "*rides Segway around*"
No more instance, though it’s there if you need it.
July 21st, 2006 at 5:44am
Sweet! I was just going to do this myself. Now I don’t have to.
July 21st, 2006 at 6:41am
I’d seen someone on IRC griping about this the other day, nice one.
July 22nd, 2006 at 8:02am
atmos: That was me griping. I wasn’t kidding when I said I was *just* going to do this myself :-)
July 23rd, 2006 at 12:20am
Nice bit of coding (your ruby fu is strong)…but I think if you’re going to use singletons, you should be forced to use ‘instance’. It serves as a good reminder that you’re using a singleton rather than a class method and if you keep having to type ‘instance’, maybe you’ll realize eventually that either you really should be using a class method or that you really should just be instantiating a real object.
Here’s a good read:
http://www.cabochon.com/~stevey/blog-rants/singleton-stupid.html
July 23rd, 2006 at 10:18am
Brad–So using
instancebuilds character? ;-)I’m well aware of Singleton’s less-than-favored status (esp. with static language programmers), but I needed this for a very specific project, and the alternative looked something like this:
(And yeah, this could be restructured to use a code block.)
Really, though, I like metaprogramming, and this seemed like fun. ;-) I’ve got a pet project I’ve been working on, and I’ve spent the past week or so shuffling the classes around, and this seemed like a neat bit of Ruby hacking. I may refactor out the proxying bit into a ClassProxy mixin or something.
July 24th, 2006 at 3:31pm
I’m curious why you needed the above line. I’ve used singletons about once in a real project (seriously), and it was a textbook case, an agent dispatcher in a mobile networks simulation.
July 24th, 2006 at 3:41pm
Shortly after I wrote that comment, I went back and refactored the code to use a block. That chunk was just bad code. I’ve been playing around with a really broad markup–>HTML renderer, trying out different internal configurations, and I had stuffed the rendering functions into a singleton. Brad was right for that particular usage–singleton was a bozo move. I’m still far from done with the refactoring, but Hampton is getting on my case about spending all my time fiddling with classes, so I’m putting it off for later.
That said, I am using this code (refactored out into SingletonProxy) for a data object which will be tossed around by a few BackgrounDRb processes, and I think that’s a valid usage, especially since the Singleton mixin is thread-safe. Brad’s point remains valid, but I also remain lazy. ;-)
August 25th, 2006 at 5:54pm
Coda, you seem to have forgotten that Ruby classes have instance vars, which entitles you to write a singelton inside of the eigenclass. Thus, your example is effectively reduced to:
class TallMagician
class 234
TallMagician.get # => 234
August 25th, 2006 at 10:40pm
Julik–I’m not at all sure what you mean, and your code snippet confuses both me and the Ruby interpreter–did Wordpress eat some of it? 234 isn’t a valid class name, and unless you’ve already defined it elsewhere, TallMagician.get isn’t going to give you anything but a NoMethodError. What would instance variables have to do with adding singleton methods to an object? One of my favorite characteristics of Ruby is that I’m constantly replacing big chunks of my code with small, elegant bits, so I’m curious to know what you mean.
Also, on a side note: Brad–I’m using this method in Blame Game, so, uh… there. ;-)
August 26th, 2006 at 3:59pm
WordPress ate my code for breakfast.
What I meant is that you can do the class «« self trick and hop into the “class level” of methods avoiding the module altogether. Just write your methods directly onto the class.
Classes in Ruby have ivars as well by the way.
August 26th, 2006 at 4:00pm
god, I hate commen cleanups. But I hope you know what I meant. To repeat:
http://pastie.caboo.se/10447
August 26th, 2006 at 4:05pm
Julik–Sorry about WordPress munging your code; it seems to happen sometimes.
And yes, you’re right, adding simple class-level methods works fine, but that approach lacks the thread-safety provided by the Singleton mixin.
August 27th, 2006 at 5:46pm
Sure, I missed that point. Thumbs up to you Coda :-)
August 27th, 2006 at 7:49pm
Also you can’t really control when a class is initialized, whereas a class with the Singleton mixin isn’t initialized until you need it.
That said, your approach is far easier and is probably the right approach given that the vast majority of Rails programming doesn’t involve concurrency.