Inheritance vs Encapsulation

E

Emiel van de Laar

--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Greetings,

Lately I've been pondering about the trade offs one must make when
implementing classes which maintain internal collections. My question
is, when is it wise to inherit and when to encapsulate. I've attached
some sample Ruby code to demonstrate my thinking. My current issues
are with C# but I believe it to be a general OO issue.

Inheriting from collection classes, i.e. Array, Hash, is simple, fast
and takes relatively little code. However every method available is
exposed to the implementing class. Something we don't always want.

Encapsulation(by association) prevents method pollution by exposing
only what is needed, which gives us more control. For code completion
this is nice because we aren't confronted with an excessive amount of
methods; we only see what is needed.

Another benefit of encapsulation is that you can replace your internal
collection at a later stadium with an alternative implementation,
perhaps for performance reasons. As long as the interface to the
implementing class remains the same you are free to do as you
please internally.

At times though, it can be a pain exposing all the methods we need;
sometimes we need them all. I know Ruby offers us the "method_missing"
feature which I have used a lot, however, as far as I know C# doesn't
offer me such a luxury.

Thanks in advance.

Cheers,

Emiel
--
Emiel van de Laar
PGP pubkey: %finger (e-mail address removed)

--mYCpIKhGyMATD0i+
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="zoo.rb"

class Duck
attr_accessor :name

def initialize name
@name = name
end
end

#
# Inheritance
#
class SomeDucks < Array
end

#
# Encapsulation
#
class MoreDucks
def initialize
@ducks = []
end

def << duck
@ducks << duck
end

def shift
@ducks.shift
end

def each
@ducks.each { |duck| yield duck }
end
end

class Zoo
def initialize
@some_ducks = SomeDucks.new
@more_ducks = MoreDucks.new

%w{daffy donald hewey}.each do |name|
@some_ducks << Duck.new(name)
@more_ducks << Duck.new(name)
end
end

def display_ducks
@some_ducks.each { |duck| puts duck.name }
puts "--"
@more_ducks.each { |duck| puts duck.name }
end
end

if __FILE__ == $0
Zoo.new.display_ducks
end


--mYCpIKhGyMATD0i+--
 
R

Robert Klemme

Emiel van de Laar said:
Greetings,

Lately I've been pondering about the trade offs one must make when
implementing classes which maintain internal collections. My question
is, when is it wise to inherit and when to encapsulate. I've attached
some sample Ruby code to demonstrate my thinking. My current issues
are with C# but I believe it to be a general OO issue.

Inheriting from collection classes, i.e. Array, Hash, is simple, fast
and takes relatively little code. However every method available is
exposed to the implementing class. Something we don't always want.

Encapsulation(by association) prevents method pollution by exposing
only what is needed, which gives us more control. For code completion
this is nice because we aren't confronted with an excessive amount of
methods; we only see what is needed.

Another benefit of encapsulation is that you can replace your internal
collection at a later stadium with an alternative implementation,
perhaps for performance reasons. As long as the interface to the
implementing class remains the same you are free to do as you
please internally.

Basically you named all important arguments, so the conclusion is: use
delegation. Even more so since it is so simple in Ruby (see below).
Inheritance is almost always the wrong choice here.

Bertrand Meyer, the OO guru and inventor of Eiffel, has a different
approach to this: he prefers inheritance and calles it "implementation
inheritance". But you need a language in which you can do it. The
difference between Eiffel and other languages is that Eiffel allows to
hide methods that you don't want accessible from the outside for a sub
class instance. Personally I dislike that because that makes naming a
inheritance relationship "is a" questionable, because the interface of the
sub class is not a superset of the super class's interface and thus both
are not compatible (see also "Liskov substitution principle").
At times though, it can be a pain exposing all the methods we need;
sometimes we need them all. I know Ruby offers us the "method_missing"
feature which I have used a lot, however, as far as I know C# doesn't
offer me such a luxury.

Think also of Delegator and SimpleDelegator, which make life even easier:

require 'delegate'
class ArrayFoo < DelegateClass(Array)
def whatever_additional_method_you_need
end
end

In certain cases (simple scripts) singleton methods may be sufficient, so
you don't need to create a new class at all:

numbers = (1..10).map { rand 10 }
def numbers.max; inject(0){|m,i| i>m ? i : m} end
puts numbers.max


Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,781
Messages
2,569,615
Members
45,294
Latest member
LandonPigo

Latest Threads

Top