Abstract Class Pattern in Ruby

In this post, I'll share how to implement the Abstract Class Pattern in Ruby.

What is Abstract Class Pattern?

Abstract Class on c2.com:

  • cannot create instance
  • provide interface

Ruby doesn't have the native support of Interface and Abstract Class. But we can still (sort of) implement it.

The Base Class

Suppose our base class is called Family:

class Family
  def initialize(_env)
    raise "Subclass must overwrite initialize"
  end
end

Set is an idiomatic way to store unique objects. We override the initialize so cannot create an instance of this base class.

Provide interface

class Family
  ...

  def self.hungry?
    raise "You need to include a function for #{self} for hungry?"
  end
end

We add a method in the base class (“interface”) that raises when the child class trying to call but not implemented it.

Track subclass

require "set"

class Family
  def self.inherited
    child_classes.add child_class
  end

  def self.child_classes
    @child_classes ||= Set.new
  end

  ...
end

The inherited is called when a subclass is created, let us keep track of child classes.

Here we use class instance variable (@child_classes) instead of class variable (@@child_classes):

A class variable (@@) is shared among the class and all of its descendants. A class instance variable (@) is not shared by the class's descendants.
💡 Difference between class variables and class instance variables?

And there seems no good use of class variables, so please try to avoid it.

With this, we will be able to find out who is hungry?:

hungry_kids = Family.child_classes.select(&:hungry?)

Any concrete examples?

Yes! I learned from the codebase of danger/danger:

That's it.

Thanks for reading! ❤


Share this post