Woody Peterson wrote:
> Rails 3 might fight in the matrix like this:
> 
>     class Person
>       def fight
>       end
>     end
> 
>     module KungFu
>       def fight
>         puts "5-fisted punch"
>         super
>       end
>     end
> 
>     module Dodge
>       def fight
>         puts "bullet-time"
>         super
>       end
>     end
> 
>     class Neo < Person
>       include KungFu
>       include Dodge
> 
>       def fight
>         puts "woah"
>         super
>       end
>     end
> 
>     Neo.new.fight # "woah"
>                   # "bullet-time"
>                   # "5-fisted punch"

That to me is awful. The logic is obfuscated; the order in which the 
methods are called is hard-coded to the order in which you mix in the 
modules, and also depends on where 'super' is placed in each method.

The Rails3 approach you show above probably works well where you just 
want to decorate existing methods with 'before' and 'after' actions, 
sort of AOP-style.

But in general, I find the composition approach much clearer and more 
widely applicable, because it's totally explicit what actions you're 
invoking and in what order. You can add, remove, replace or re-order 
actions at run-time (although you say that's not important to you), and 
for different object instances.

For debugging, a simple #inspect will show you the stack of actions 
belonging to this object. You can probably extract the relevant info 
using #ancestors in the inheritance version, but what happens if you 
want a second, orthogonal set of behaviours in your object? #ancestors 
is essentially a linear list, so you'll have to know by inspection which 
modules implement behaviour A and which implement behaviour B.

Perhaps most importantly, it also ensures separation of concerns. Mixins 
can call each other willy-nilly, and share instance variables in the 
underlying object; but if you have separate instances of @object1 and 
@object2, then they won't even know of the existence of the other object 
unless you intentionally pass them a reference. So this makes logic 
clear: call @object1 to do some work, and if you want @object2 to do 
something which depends on that, pass it the result.

I also find composition is far superior when you're unit testing and 
mocking. (I think that unit testing your module KungFu above would be 
painful).

When you learn "object oriented programming" from books or school, they 
push you down the class inheritance route. I took me a long time to work 
this out, but it is often a poor way to build real systems. It's a bit 
like being brought up a Catholic and then finally realising you don't 
have to believe in it after all.
-- 
Posted via http://www.ruby-forum.com/.