Composition: Build objects from other objects

T

Thufir

<http://www.javaworld.com/javaworld/jw-06-2001/jw-0608-java101.html> has
a discussion of composition versus inheritance which I'm trying to apply
to ruby. For discussions sake, I'll use the classes discussed in the
above linke: Car, Vehicle and Engine. Car inherits from Vehicle, of
course.

Let's say that Engine is something like:




class Engine

attr_accessor :type


def initialize ()
@type = "generic engine"
end

def vroom ()
puts "vroooooom"
end
end


Vehicle would be something like:


class Vehicle

attr_accessor :engine

def initialize ()
@engine = Engine.new
end
end


class Car < Vehicle

def initialize ()
super()
end
end


then from IRB for instance:

vw_bug = Car.new



then can you make the vw_bug go "vroooooom" and so forth? What would be
the usual way to do this in ruby? When a car is instantiated, should an
engine object get passed to the initializer method?



thanks,

Thufir
 
R

Raul Parolari

Thufir said:
then can you make the vw_bug go "vroooooom" and so forth? What would be
the usual way to do this in ruby? When a car is instantiated, should an
engine object get passed to the initializer method?

thanks,

Thufir

I do not like (at first view) the idea of this design; it leads to, as
Konrad has correctly observed, to start the car by saying:

vw_bug.engine.vroom()

Should the car user know that he has to tell the car to ask the engine
to rev up? Uhm..

Another take is the following; let's use a module Engine:

module Engine
attr_accessor :type

def initialize
@type = "generic engine"
end

def vroom
puts "vroooooom"
end
end

class Vehicle
include Engine

def initialize
super
end
end

class Car < Vehicle
def initialize
super
end
end

vw_bug = Car.new
vw_bug.vroom

I am not crazy of the 'Engine' Module, but I definitely like to ask the
VW to wroom, without worrying about what is inside,

Raul
 
T

Thufir

I do not like (at first view) the idea of this design; it leads to, as
Konrad has correctly observed, to start the car by saying:

vw_bug.engine.vroom()



Fair enough. This is pretty much how Java would do it, and, correct me
if I'm wrong, Smalltalk would probably do similarly.

Put another way: there must be a non-contrived case of composition in
Ruby? Yes?



thanks,


Thufir
 
R

Raul Parolari

Thufir said:
Put another way: there must be a non-contrived case of composition in
Ruby? Yes?

I do not really see this as a contrived example of composition; the only
thing that I did not like in the solution we examined is the name
'Engine' for a module (for a principle given by D.Black of 'adjectives
for Modules, and nouns for Classes'..). But I think that the advantage
of a Module in this case trounces the naming principle (so far; perhaps
if design continued..).

For your question about existing examples of 'Composition': is this the
eternal OO issue about 'to have' vs 'to be', and the need for objects to
'contain' other objects?

I can only mention the only Ruby application that I looked at: Rails
(not as an user, but internally, to study how Ruby can be used). In
Rails definitely we find objects which 'contain' other objects (I will
however use the name 'reference' from now on, as I detest the word
'containment'); for example:
- the Dispatcher references the objects request, response, and the
Controller class name
- the Controller (besides request, response received from the
Dispatcher) creates an Anonymous Class View and then it instantiates it;
so it references the instance view (which allows cool tricks for
instance variables passing).

However, reading Rails, this is NOT what strikes the reader (what is the
big deal about objects referencing other objects?), but rather the
explosive combination of INHERITANCE + MIXIN + METAPROGRAMMING to glue
the system together; for example, what modules ActionController::Helpers
and ClassMethods do on behalf of the Controller (and View).

Here is where Ruby shows facets that other languages cannot even dream
of (and I doubt that we should worry about mimicking with Ruby what
others are doing..); but I am getting off theme.

I hope others will give you more useful examples,
good luck

Raul
 
R

Rick DeNatale

I do not really see this as a contrived example of composition; the only
thing that I did not like in the solution we examined is the name
'Engine' for a module (for a principle given by D.Black of 'adjectives
for Modules, and nouns for Classes'..). But I think that the advantage
of a Module in this case trounces the naming principle (so far; perhaps
if design continued..).

I think there are two issues here. First , how to model the
relationship between car and engine, and second what the interface to
the resulting car should be.

Including an Engine in a car using a module seems wrong to me.

Modules add behavior to an object. That, I think, is the heart of
David's analogy of Modules as adjectives, the Enumerable module adds
enumerability to an object, the Comparable module adds
comparability...

Cars don't behave like engines, they use the engine internally to
provide their power source. Modeling the engine as a separate class
allows different models of cars with different types of engines,
likely created using some form of Factory pattern.

vw_bug.engine.vroom violates the 'law' of Demeter, which says that
it's bad style to reach through one object to get at another. The
classic example is modeling a paperboy collecting money from a
customer.

Bad: the paper boy shouldn't be grabbing money from the customer's wallet.

class PaperBoy
def collect_from(customer)
receive_payment(customer, customer.wallet.remove(2.dollars))
end
end

instead of
class Paperboy
def collect_from(customer)
receive_payment(customer, customer.pay(2.dollars))
end
end

So I think that to start the car it should be something like

vw_bug.start

And the start method would cause the engine to start, producing Vrooom
as a side effect, depending on the particular engine which was
installed by the factory.
 
L

Liam

On Sat, 17 Nov 2007 14:36:50 +0900, Raul Parolari wrote:

Fair enough. This is pretty much how Java would do it, and, correct me
if I'm wrong, Smalltalk would probably do similarly.

Put another way: there must be a non-contrived case of composition in
Ruby? Yes?

What are you looking for? Composition is basically accomplished by
instance variables, even for less contrived cases. As for the
interface, to say TMTOWTDI is an understatement.

require 'forwardable'
class Engine
def vroom
puts "vroom"
end
end
class MustangEngine
def vroom
puts "rumble"
end
end
class Vehicle
extend Forwardable
def_delegator :mad:engine, :vroom
attr_accessor :engine
def initialize(engine)
@engine=engine
end
end

v=Vehicle.new(Engine.new)
v.vroom
v.engine=MustangEngine.new
v.vroom


As far as comparison to Java, this example uses duck typing. As long as it vrooms, you can use it as an engine for your vehicle.
 
J

Jeremy McAnally

Doesn't seeking to mix a method in like that totally break
encapsulation though (and possibly offend our good friend Demeter)?
Why does the whole Car need to know anything about what the Engine is
doing except that it's doing what it needs to be doing and give it
facilities to do what is needed?

I know you're going for composition here, but maybe I'm not seeing the
benefit of it or this is a bad example?

--Jeremy

Fair enough. This is pretty much how Java would do it, and, correct me
if I'm wrong, Smalltalk would probably do similarly.

Put another way: there must be a non-contrived case of composition in
Ruby? Yes?



thanks,


Thufir



--
http://www.jeremymcanally.com/

My books:
Ruby in Practice
http://www.manning.com/mcanally/

My free Ruby e-book
http://www.humblelittlerubybook.com/

My blogs:
http://www.mrneighborly.com/
http://www.rubyinpractice.com/
 
T

Thufir

v=Vehicle.new(Engine.new)
v.vroom
v.engine=MustangEngine.new
v.vroom


As far as comparison to Java, this example uses duck typing. As long as
it vrooms, you can use it as an engine for your vehicle.

Hehe, fair enough. Ok, enough Java, it was just an entry point (although
I would be interested to hear how this is addressed in other OOP oriented
languages). What stands out about the above is:

1.) I don't follow all of the syntax, some of it's over my head (which
is certainly ok).

2.) The engine is being instantiated *outside* of the class definition.
I was assuming that the engine would be created in the vehicle's
initialize method. The above technique is to specify the engine when the
car is "manufactured" by passing a new instance of Engine into the new
Vehicle instance.



-Thufir
 
R

Raul Parolari

Rick said:
Cars don't behave like engines, they use the engine internally to
provide their power source.

You are right; the word 'behave' vs 'use' is the key. I could not resist
letting the 'vroom' reach the Engine without any intervention from
Vehicle; and I let expediency trounce principle. Your eloquent point is
taken.
vw_bug.engine.vroom violates the 'law' of Demeter, which says that
it's bad style to reach through one object to get at another
So I think that to start the car it should be something like
..
vw_bug.start

And the start method would cause the engine to start, producing Vrooom
as a side effect, depending on the particular engine which was
installed by the factory.

Here we disagree; not on (what I prefer to call) the "principle of least
knowledge", but on the example at hand. Removing the 'vroom' method from
the user is a bad application of that principle; customers do not always
'vroom' to start the car, but to assert something (that I hope I don't
need to explain). It is enough to watch a city intersection for a few
minutes at a traffic light to see that. Removing the 'vroom' is not an
option.

[In fact, I liked the module Engine, exactly because of that: it removed
the need to put knowledge of 'vroom' in the Vehicle! but enough on
trying to justify my sin].

I agree that the problem 'vw_bug.engine.vroom' is that it breaks
encapsulation.
It seems that we are left with having to put a method in Vehicle to
'vroom', just to transmit the command to the engine..

============
require 'forwardable'
..
class Vehicle
extend Forwardable
def_delegator :mad:engine, :vroom
attr_accessor :engine
def initialize(engine)
@engine=engine
end
end

v=Vehicle.new(Engine.new)
v.vroom
v.engine=MustangEngine.new
v.vroom
As far as comparison to Java, this example uses duck typing. As long as
it vrooms, you can use it as an engine for your vehicle.

Interesting: this solves the problem of explicitly writing a 'vroom'
method in Vehicle just to pass it to the Engine. I am not totally
comfortable with the thought of Car 'delegating to' (as opposed to
'having') an Engine, but it is very elegant.

=========

Personally, I learnt/conclude this (in view of some vagueness in other
comments that seem to say the Composition is not needed):

1) Thufir was right (aside on the details for how to 'vroom'), in seeing
the Engine as a Composition problem for Vehicle. There is an instance
variable @engine in Vehicle! (shame on me for attempting to remove it
:).

2) we can use 'Delegation' to avoid silly 'bridge methods' in Vehicle,
to allow the user reach the Engine, when needed.

Thanks!

Raul
 
P

Pat Maddox

I do not like (at first view) the idea of this design; it leads to, as
Konrad has correctly observed, to start the car by saying:

vw_bug.engine.vroom()

Should the car user know that he has to tell the car to ask the engine
to rev up? Uhm..

Another take is the following; let's use a module Engine:

module Engine
attr_accessor :type

def initialize
@type = "generic engine"
end

def vroom
puts "vroooooom"
end
end

class Vehicle
include Engine

def initialize
super
end
end

class Car < Vehicle
def initialize
super
end
end

vw_bug = Car.new
vw_bug.vroom

I am not crazy of the 'Engine' Module, but I definitely like to ask the
VW to wroom, without worrying about what is inside,

Raul


I think this is a pretty bad approach. Consider the following example
(keep in mind I know nothing about cars):

module Engine
attr_reader :engine

def initialize
@engine = :generic
end

def vroom
puts "vrooooooom"
end
end

module Transmission
attr_reader :transmission

def initialize
@transmission = :silky_smooth
end

def vroom
puts "smooth like butta"
end
end

class Car
include Engine
include Transmission

def initialize
super
end
end

By "composing" it with modules like that, we end up with a seriously
broken car - one with no engine! Clearly this approach won't work.
Part of it is due to how you set stuff up in initialize...but anyway,
the point is that you don't really use modules for this kind of
composition.

A far better approach would be to have different engine and
transmission objects.

class Engine
def vroom
"vrooooooom"
end
end

class Transmission
def vroom
"smooth like butta"
end
end

class Car
def initialize(engine, transmission)
@engine = engine
@transmission = transmission
end

def vroom
"we go #{@engine.vroom}, #{@transmission.vroom}"
end
end

c = Car.new Engine.new, Transmission.new
c.vroom # => "we go vrooooooom, smooth like butta"

if you want to create a car with its parts already initialized, you
can create a factory method:

def Car.basic
self.new Engine.new, Transmission.new
end

The basic idea is that you don't really want to make the car object
responsible for a bunch of stuff...you want a bunch of little objects
that know how to do one thing well.

Pat
 
L

Liam

Hehe, fair enough. Ok, enough Java, it was just an entry point (although
I would be interested to hear how this is addressed in other OOP oriented
languages). What stands out about the above is:

1.) I don't follow all of the syntax, some of it's over my head (which
is certainly ok).

It makes use of fowardable[1] from the standard library.
2.) The engine is being instantiated *outside* of the class definition.
I was assuming that the engine would be created in the vehicle's
initialize method. The above technique is to specify the engine when the
car is "manufactured" by passing a new instance of Engine into the new
Vehicle instance.

This is more of a design decision. Providing outside access allows for
inversion of control. Perhaps you would prefer to use a factory for
your car?

class Factory
def self.build_car
Car.new(Engine.new)
end
end
class Mechanic
def soup_up(car)
car.engine=MustangEngine.new
end
end

falcon=Factory.build_car
car.vroom
joe=Mechanic.new
joe.soup_up falcon
car.vroom

The interface is of course rather arbitrary. Take a look at logr4[2]
for some interesting composition.


[1] http://ruby-doc.org/stdlib/libdoc/forwardable/rdoc/files/forwardable_rb.html
[2] http://log4r.sourceforge.net/
 
R

Raul Parolari

Pat said:
I think this is a pretty bad approach. Consider the following example
(keep in mind I know nothing about cars):

Pat, 1 hour before your letter, I wrote a clear admission of sin :)

In particular, there is this sentence in Rick's letter: "a car 'uses'
the Engine, does not 'behave' as an Engine". That settled the issue.


However, let's remember that there is often another view for modeling
the world.
I just found in D.Black's book a very pertinent example (p. 172):

module SelfPropelling # was a Class..
end

class Vehicle
include SelfPropelling
end

class Truck < Vehicle
end

Of course, I let him elaborate, if he has a chance,

Raul
 
T

Trans

I think this is a pretty bad approach. Consider the following example
(keep in mind I know nothing about cars):

module Engine
attr_reader :engine

def initialize
@engine = :generic
end

def vroom
puts "vrooooooom"
end
end

module Transmission
attr_reader :transmission

def initialize
@transmission = :silky_smooth
end

def vroom
puts "smooth like butta"
end
end

class Car
include Engine
include Transmission

def initialize
super
end
end

By "composing" it with modules like that, we end up with a seriously
broken car - one with no engine! Clearly this approach won't work.
Part of it is due to how you set stuff up in initialize...but anyway,
the point is that you don't really use modules for this kind of
composition.

A far better approach would be to have different engine and
transmission objects.

class Engine
def vroom
"vrooooooom"
end
end

class Transmission
def vroom
"smooth like butta"
end
end

class Car
def initialize(engine, transmission)
@engine = engine
@transmission = transmission
end

def vroom
"we go #[email protected]}, #[email protected]}"
end
end

c = Car.new Engine.new, Transmission.new
c.vroom # => "we go vrooooooom, smooth like butta"

if you want to create a car with its parts already initialized, you
can create a factory method:

def Car.basic
self.new Engine.new, Transmission.new
end

The basic idea is that you don't really want to make the car object
responsible for a bunch of stuff...you want a bunch of little objects
that know how to do one thing well.

These module examples are flawed. First of all, we need to know what
the use case is. Is this a Nasa Pro Racing Game or a Traffic Light
Simulator? If the later a simple module would probably do fine b/c no
one's planning on swapping out whole engines (i.e. the car just needs
some engine qualities like vroom). We might write:

module Engine
attr_accessor :type

def vroom
case type
when :small
"vrooom!"
when :big
"VROOOOOOOOOM!"
else
raise HunkOJunkError
end
end
end

Now if we need something more fancy. A module still might be useful to
define "Engineability". You know, a thing that has #fuel and an
#ignition switch can handle Engineability, and that can set up the
delegation we want.

module Engineability
attr :engine

def install_engine(type, ignition, fuel)
@engine = Engine.factory(type, ignition, fuel)
end

def vroom(level=10)
ignition.turn unless engine.on?
engine.throttle += level
end
end

So modules can be useful in a variety of ways.

And really the bottom line is a simple fact, Model != Reality. Try to
use the simplest abstraction possible. We don't always need to map
real objects <=> program objects, when a simpler abstraction would be
enough --eg. a function would do, but we went and created a whole
Engine class just to go #vroom ;)

2c.
T.
 
T

Thufir

1) Thufir was right (aside on the details for how to 'vroom'), in seeing
the Engine as a Composition problem for Vehicle. There is an instance
variable @engine in Vehicle! (shame on me for attempting to remove it .

Well that's very complimentary of you :)
2) we can use 'Delegation' to avoid silly 'bridge methods' in Vehicle,
to allow the user reach the Engine, when needed.


I need to learn more about Delegation, and was very interested in the
Factory pattern. It seemed amazingly apropos, given that cars are
manufactured in a factory!


thanks,

Thufir
 
T

Thufir

falcon=Factory.build_car
car.vroom
joe=Mechanic.new
joe.soup_up falcon
car.vroom

The interface is of course rather arbitrary.

I really like that interface, thanks Liam.



-Thufir
 
T

Trans

Well that's very complimentary of you :)


I need to learn more about Delegation, and was very interested in the
Factory pattern. It seemed amazingly apropos, given that cars are
manufactured in a factory!

*shakes head*

T.
 
R

Raul Parolari

Well that's very complimentary of you :)

Just stating a fact in the summary I attempted to do (which does not
include the interesting and alternative view given by Trans, which came
after).
I need to learn more about Delegation..

You can learn what Delegation is very easily; the real work in the
Forwarding module is done in a few lines; I wrote a mini-version of the
module removing exception processing and others things not necessary, to
play with it a bit.

Look at this simplified version (used in the example below):

module Simple_Forwardable

def def_delegator(accessor, method, ali = method)

module_eval(<<-EOS)
def #{ali}(*args, &block)
#{accessor}.send:)#{method}, *args, &block)
EOS
end
end

Believe it or not, these 6 lines are the heart of Forwardable; in short,
in case you are not familiar with some things:

a) the method 'send' is used to 'send' a method name (eg, a method that
you have in a string or symbol, for example that you collected from a
web form or cmd line) to an object.
b) the module_eval allows to define dynamically methods using
interpolation.
That's it!

If the syntax looks a bit mysterious at first, look at 'accessor' as our
@engine object and 'method' as 'vroom'. Notice that we can even name an
alias for 'vroom' if in the Vehicle class we prefer a different name
(say, 'rev'); and,we can even pass arguments (eg, we can pass the level
of the vroom we want). Both are done in the file below.

Below is the file that you may want to play with, if you find it useful
(it combines the 2 examples given by Liam in one file; perhaps there are
too many options, but you will decide that).

Good luck! and perhaps let us know how your project goes and if stood up
well to the shock with 'reality' :)

Raul


module Simple_Forwardable

def def_delegator(accessor, method, ali= method)
module_eval(<<-EOS)
def #{ali}(*args, &block)
#{accessor}.__send__:)#{method}, *args, &block)
end
EOS
end

end

# from Liam's examples, combined
class Engine
def vroom(level)
puts "vroom #{level}"
end
end

class MustangEngine
def vroom(level)
puts "rumble #{level}"
end
end

class Vehicle
extend Simple_Forwardable

def_delegator :mad:engine, :vroom, :rev
attr_accessor :engine

def initialize(engine)
@engine=engine
end
end

class Factory
def self.build_car
Vehicle.new(Engine.new)
end
end

falcon = Factory.build_car
falcon.rev('mid') # => vroom mid

v = Vehicle.new(Engine.new)
v.rev('high') # => vroom high

# changing the engine
v.engine = MustangEngine.new
v.rev('low') # => rumble low
 
R

Raul Parolari

Thufir said:
Thank you for the information.

I was looking at <http://www.juixe.com/techknow/index.php/2006/06/15/
mixins-in-ruby/> and it has a good discussion and explanation of
"include" versus "extend" for mixins of modules, which I find quite
informative. Bit off topic, but kinda related.

Hi, Thufir

I browsed the site, and I understand that it is very useful for
somebody coming from Java to begin to picture what 'include/extend' are
in Ruby.

However, beware that this kind of information is useful and 'dangerous'
at the same time if taken literally; eg, the example it gives of a
Module hardcoding the class name (that is using it) is a bit.. eery
(although I understand that he is just trying to show the mechanics of
interaction module-class).

In Ruby, a Module of course has no preconceived notion of the classes
using it; but, at the same time, if it needs to 'talk' to the class
using it, it can (see the humble Enumerable module; you write a class
including it, you define 'each', and suddenly.. you get a bounty of
methods free; the module is in fact calling your 'each').

Not to mention Rails, were the interaction between Classes and Modules
becomes surreal (modules injecting code into classes at the moment that
those classes are being created..).

So, perhaps take that site with a grain of salt, and, when you have a
chance, try a good Ruby tutorial on Classes/Modules; a great thing in
Ruby is that one can read and test at the same time with irb even the
most complex inheritance scheme (so one does not get asleep in an 'ocean
of abstractions' :).

Regards,
Raul
 
T

Thufir

However, beware that this kind of information is useful and 'dangerous'
at the same time if taken literally; eg, the example it gives of a
Module hardcoding the class name (that is using it) is a bit.. eery
(although I understand that he is just trying to show the mechanics of
interaction module-class).

In Ruby, a Module of course has no preconceived notion of the classes
using it; but, at the same time, if it needs to 'talk' to the class
using it, it can (see the humble Enumerable module; you write a class
including it, you define 'each', and suddenly.. you get a bounty of
methods free; the module is in fact calling your 'each').


Thank you for the heads up, definitely something for me to come back to :)


-Thufir
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top