A Question
Lately, this has become something of a design pattern for me: A base class describes a type of behavior, and rather than subclasses inheriting the base class’s behavior (which tend to just raise NotImplementedErrors), the subclasses override the base class’s methods to do their own thing. The catch, of course, is that some of the base class’s methods are not, in fact, just stubs. Some of them actually dispatch the message just received to each of the subclasses. So if you send GentleCMS::Cache the :clear message, that message will actually get relayed to GentleCMS::ResponseCache, GentleCMS::ResourceCache, and GentleCMS::RouteCache, thus clearing out all caching systems within GentleCMS. This allows for either selective or indiscriminate clearing of the cache. Very useful.
The problem is that my method for finding subclasses is slow, and I was hoping that you, Dear Readers, might have some suggestions for how I might improve the performance of this method:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Module #:nodoc: # Returns a list of modules and classes that descend from this module. def descendants descendant_modules = [] ObjectSpace.each_object do |object| next if !object.kind_of? Module next if object == self descendant_modules << object if object.ancestors.include? self end return descendant_modules end end |
I tend to cache the results of calling this method, so it doesn’t have a huge performance hit during normal operation, but startup times have become rather surprisingly long.
Update: I discovered that ObjectSpace.each_object could take an optional type parameter. The code runs much faster now:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Module #:nodoc: # Returns a list of modules and classes that descend from this module. def descendants descendant_modules = [] ObjectSpace.each_object(Module) do |object| next if object == self descendant_modules << object if object.ancestors.include? self end return descendant_modules end end |
Update: Cool, that took the worst-case startup times down from 12 seconds to a worst-case startup time of 3 seconds. Not bad, a 400% overall performance improvement from changing two lines of code. I can live with that.
I have the same issue for STI subclasses on ActiveRecord objects. I added @@descendents to the SuperClass with an accessor add_descendent. Then each subclass calls superclass.add_descendent(self). Looking for a better technique still.
Yeah, that technique works, but there’s no doubt but that it’s ugly. I suppose it’s not as bad if
add_descendentis a protected method, but even then…I certainly wouldn’t mind it if someone figured out an even more efficient way of doing it than what I’m using now.
You could create an inherited method in the superclass. It will be called automatically when you create the subclass.
Your comment spam protection won’t seem to let me post any code.
Leave a Response