From: Trans Date: 2007-11-18T07:51:38+09:00 Subject: Re: Composition: Build objects from other objects On Nov 17, 5:08 pm, "Pat Maddox" wrote: > On Nov 16, 2007 9:36 PM, Raul Parolari wrote: > > > > > Thufir wrote: > > > > 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 > > > -- > > Posted viahttp://www.ruby-forum.com/. > > 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. 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.