Close and Go BackBack to Viget

Inheritance is Unlikely

Justin Marney
Justin Marney, Web Developer, June 05, 2008 8 What do you do when you identify common sets of shared behavior across several classes?

A common solution is to move the shared code into a base class and inherit.
# Filled with api interaction behavior
class Base
  def api_specific_behavior
    puts "connect to api"
  end
end

class User < Base
  def user_specific_behavior
    puts "I am a user"
  end
end
The problem with inheritance in this situation lies primarily in a subtle violation of the Liskov Substitution Principal which essentially states that inheritance should implement an "is-a" relationship. If we have a class called User that inherits from Base, are we trying to model the part of our domain where a User is a Base? This doesn't make sense given that we never actually have any Bases (i.e. instances of Base).

We created the class to share behavior not to model an aspect of the domain. Unfortunately, sharing behavior in this way creates a brittle, highly-coupled, and most importantly unnecessary relationship between the parent and child. The real pain of using this technique comes when you want to model a true inheritance relationship on the child. For example, we needed ActiveRecord objects that could also interact with an external API. Since both ActiveRecord and our design required classes to inherit from a base class we were forced to revise our design.

A better solution is to move shared behavior into a module.
module ApiInteractions
  def api_specific_behavior
    puts "connect to api"
  end
end

class User
  include ApiInteractions

  def user_specific_behavior
    puts "I am a user"
  end
end
The pattern of using inheritance to share behavior most likely stems from the absence of a non-coupling behavior sharing mechanism in older OO languages. Modules specifically allow you to share behavior between classes without introducing an inheritance relationship. The behavior of a User in your domain doesn't include connecting to an HTTP server so why should that functionality be hard-wired into your User model via inheritance? Modules allow you to move these types of concerns out of your domain and into reusable packages.

What about the initialization method in my base class?
Any behavior in the initializer of the base class is specific to the concerns the base class is handling. That behavior should be available as part of the modules api and called from the host class.

Move initializer behavior into a method and call from the host class.
module ApiInteractions
  def api_initialization_behavior
    api_specific_behavior
  end

  def api_specific_behavior
    puts "connect to api"
  end
end

class User
  include ApiInteractions

  def initialize
    api_initialization_behavior
  end
end
The above code highlights one last issue you should be aware of when refactoring. It is possible to couple your modules to your host classes. When you apply this technique avoid having a large (or even medium sized) interface between the module and its host. In the above case one method, api_initialization_behavior, is enough to get the job done. You should be shooting for one or two methods. Avoid using instance variables as well, use accessor methods instead. This way your module becomes more flexible and your model classes are not filled with code that is tightly coupled to the implementation of the module.

Wait a minute, ActiveRecord uses a Base class — it must be ok?
You might be wondering why ActiveRecord requires you to inherit from a base class when object persistence isn't really part of your domain model. Trust me, you aren't alone. Take a look at DataMapper's API for an alternative, module-based implementation.

The ideas in this post were inspired by the Pragmatic Studio Advanced Ruby Training. I have been hacking on Ruby for two years now and considered myself to be fairly adept in its mystical ways. Within the first ten minutes of training I had already learned about things I never knew existed. Want a good run down of Ruby C code conventions? Interested in coding your own custom control flow structures? Ever wanted to know how to use Drb and Rinda? This class will teach any Ruby programmer something. I highly recommend you attend if you have the opportunity.
Michael Granger said on 06/05 at 08:06 PM

Because of the way mixins are implemented in Ruby, you can use #initialize in the Module just like in a class. Then, all you have to remember to do in your class is call ‘super’ from your own #initialize if you have one. If you don’t, you don’t need to do anything special:

module Glar
def initialize
puts “I am glarring”
end
end

class Hloom
include Glar
end

class Bglon
include Glar
def initialize
puts “I am a Bglon”
super
end
end

puts “Creating a Hloom”
Hloom.new
puts “Creating a Bglon”
Bglon.new

### Output:
# Creating a Hloom
# I am glarring
# Creating a Bglon
# I am a Bglon
# I am glarring

delizade said on 06/06 at 06:32 AM

hi,
thanx for this topic. actually, I wanted to hear your thoughts about inheritance / composition matters on ActionScript. is there any clue or succestions about this topic connected with AS.

because most of time, I came across this matter as a problem. most of time i mixed them. and now, I have lots of classes that inherits some base casses, and as u can guess, I lost my control on that. I have to decide on a solution. I have to see my ways about this matter. where should I go.

I understand “is a” and “has a” subjects. but, still, I confused.

I think I must use composition rather than inheritance. cus, as you said above “using inheritance makes a highly-coupled relations” and this stuaition is not for me as a not enought settled-knowlage person.

regards.

Justin Marney said on 06/06 at 08:45 AM

@Michael I suggested making any initialization behavior part of the module API for two reasons.  The main reason lies in the fact that you can include multiple modules.  This feature is key to allowing you to factor different concerns into orthogonal chunks of behavior and pepper them about your classes.

module Wizard
def initialize
puts “wiz”
end
end

module Pong
def initialize
puts “ping”
end
end

class Scarf
include Wizard
include Pong

def initialize
super
end
end

>> s = Scarf.new
ping
=> #<Scarf:0x35889c>

The reason Wizard’s initialize never fired is due to the way Ruby handles inserting modules into a class’ inheritance tree when include is called.  In fact, if you try

>> Scarf.ancestors
=> [Scarf, Pong, Wizard, Object, Kernel]

You can see that Scarf’s super is the most recently added module.  You could also call super in Pong but that introduces a brittle coupling between Pong and Wizard.  Which brings me to the second reason I like using a modules API to handle initialization: it is explicit.  When I see a class with 5 includes and it just calls super it isn’t immediately clear what is going to happen.  In some cases I might not need initialization behavior from every module.  Using a well named module API I can see exactly what is happening when my class is instantiated.

Michael Granger said on 06/06 at 01:42 PM

I think we have different ideas about what “brittle coupling” means. If you put ‘super’ in both of the modules, they use Ruby’s inheritance to determine which #initialize comes next. If a module doesn’t need initialization, you just don’t define an #initialize for it. To me, decoupled means that the including class doesn’t need to know anything about the included modules; which is not the case if you are required to not only know if the module needs initialization, but also to call a specially-named method to achieve it. Using Ruby’s native initializer, you can include a module without adding anything else to #initialize, even at runtime:

module Wizard
def initialize
puts “wiz”
super
end
end

module Pong
def initialize
puts “ping”
super
end
end

class Scarf
include Wizard

def initialize
super
end
end


irb(main):022:0> s = Scarf.new
wiz
=> #<Scarf:0x335108>
irb(main):025:0> # Wait, all Scarves should Pong, too:
irb(main):026:0* Scarf.send( :include, Pong )
=> Scarf
irb(main):027:0> s2 = Scarf.new
ping
wiz
=> #<Scarf:0x32ebf0>

As to your assertion that “it isn’t immediately clear what is going to happen” when you call ‘super’ from #initialize: you’re making the case against yourself. Any specifics you add to the including class that would express what the including module is doing is coupling that class to the module. The class shouldn’t have to know what initialization is needed (if any) by the modules it includes, as that knowledge more properly inheres in the module itself.

TMTOWTDI even in Ruby, I guess. :)

Priit Tamboom said on 06/07 at 01:09 AM

Thanks for good writeup and I give +1 for Michael for pointing out even more elegant way of doing it.

>You should be shooting for one or two methods.

Currently I’m writing my own openid-wrapper module (going to publish it at gitorious soon :-) and I got confused from your blogpost right now: do you mean I should keep module public methods low (say max 1-2)? Or you were talking about methods which serve the initialize part?

/I3az/ said on 06/07 at 08:04 AM

Great article.

You may find this video presentation “Your Doing OO Wrong” at recent YAPC::Asia of interest....  http://conferences.yapcasia.org/ya2008/talk/1134

NB.  Don’t be put off because its Perl.  Uses Moose which makes it very easy to understand.

Priit Tamboom said on 06/08 at 02:15 AM

@l3az thanks for a great videolink. Can we call it aspect-oriented programming?

/I3az/ said on 07/01 at 10:40 AM

Certainly smells like AOP because Moose does run on top of a MOP (Meta Object Protocol).

PS. For more info about Moose see moose.perl.org.

Trackback URL: http://www.viget.com/trackback/1139/

Comments for this entry were closed after 60 days.

A Development Community for Viget Labs and Beyond

Every team member here at Viget Labs strives to be an innovator. We members of the development team are no different - that's why we're constantly engaging in community discussions and exploring the unknown that is the next generation of open-source web applications.

Viget Is Hiring!

Viget has job openings for Ruby Developers, Interns, and Front-End Developers. Learn More »

Recent Comments

I think that polymorphic_url(@commentable, :anchor => “comment_#{@comment.id}") should work. You can also refactor the “comment_#{@comment.id}” to a separated method, like dom_id, which returns the dom identifier of the comment.