Skip to content

HOWTO - Get a list of a class’s subclasses

I recently came across a situation where I had an AbstractClass, an I wanted to know all of the classes that had inherited from it. There were lots of implementations on the web, but that weren’t exactly what I wanted, or they used ObjectSpace to get ALL the classes, and see if the interesting one was in its ancestors.

I only needed it one-level deep, but it would be fairly easy to extend it for more.

class ParentClass
  def self.subclasses
    @subclasses ||= Set.new
  end

  def self.inherited(subclass)
    subclasses << subclass
  end
end

class ChildA < ParentClass; end
class ChildB < ParentClass; end

ParentClass.subclasses
# => #<Set: {ChildA, ChildB}>

{ 6 } Comments

  1. Liam Morley | February 4, 2009 at 12:14 pm | Permalink

    Hmm, traditionally in OOP this is a bad idea, so I’m curious- why did you find this necessary?

  2. Dan Kubb | February 4, 2009 at 2:11 pm | Permalink

    @Liam: I can think of one case where I’ve needed this when writing DataMapper.

    DataMapper lets you define properties in each class or subclass when using single table inheritance (STI). When you ask a class to auto-migrate itself (create a table to contain it’s instances) the job is delegated to the top-most ancestor, what DM calls the base model. The base model then needs to ask all of it’s descendants for their properties so it knows what columns to create in the table. It uses this idiom to pull in all of it’s descendants.

    Granted it’s pretty rare, but useful when you need it.

  3. Liam Morley | February 4, 2009 at 5:39 pm | Permalink

    @dkubb your case sounds like a job for the Template Method pattern.

    I’m not saying that there’s never a case for this, just saying it’s rare, and you should really know that you need to do this before you do. :)

  4. cies breijs | February 5, 2009 at 5:19 am | Permalink

    i have some questions regarding this..

    you use a instance var, @subclasses, in a class method, self.subclasses… is that not a strange thing to do? i expected a class var @@subclasses :-)

    i was looking something along the lines of this, because i have DM model (hi dkubb!!) that can have a subclass structure. as some point i want the user to pick which particular subclass he’d like to use. in this case i need to know all the subclasses of this model — this solution is by far the most pretty solution i’ve seen so far.

    cheers, _cies.

  5. cies breijs | February 5, 2009 at 9:08 am | Permalink

    i cannot figure out why @@subclasses doesn’t work and @subclasses does..

    :-$

  6. Paul Sadauskas | February 5, 2009 at 1:01 pm | Permalink

    @Liam: Specifically, I’m using it here: http://github.com/paul/dm-core/blob/ac0475928278a32969634ed075561c196eb11c57/lib/dm-core/conditions/comparisons.rb#L10

    I wanted a method to be able to easily create an object, the type of which is based on a param. I also wanted it to be trivial for a plugin author to add more of these classes, and my method to automatically pick that up.

    The traditional way to do this would be to use a case statement, but that would be much harder to extend. I’d have to have a registration method, and an author would have to remember to register their class. This way, someone can just do:

    class FooComparison < AbstractComparison; slug :foo; end
    

    Then they’re able to use their Foo as a comparison automatically. Condition.new(:foo, a, b)

Post a Comment

Your email is never published nor shared. Required fields are marked *