From: Robert Klemme Date: 2010-09-24T15:55:57+09:00 Subject: Re: composition vs 'leaf-class mixins' (vs class inheritance) On Fri, Sep 17, 2010 at 10:39 PM, Woody Peterson wrote: > I have a design question inspired by Rails 3 internals. In Design > Patterns in Ruby, the Strategy pattern is cited as a way to vary an > algorithm following the oft-spoken "prefer composition over inheritance" > advice. For a concrete example, let's say we wanted to fight in the > matrix: > >    class Person >      attr_accessor :skills > >      def initialize(skills) >        @skills = skills >      end > >      def fight >        @skills.each { |skill| skill.invoke } >      end >    end > >    class KungFu >      def invoke >        puts "5-fisted punch" >      end >    end > >    class Dodge >      def invoke >        puts "bullet-time" >      end >    end > >    class SeeTheMatrix >      def invoke >        puts "woah" >      end >    end > >    me = Person.new([]) >    me.fight # i've got nothing > >    neo = Person.new([KungFu.new, Dodge.new, SeeTheMatrix.new]) >    neo.fight # "5-fisted punch" >              # "bullet-time" >              # "woah" > > 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 would be a poor Neo who's skill set never changes. > Maybe a class-based inheritance implementation would be: > >    class Person >      def fight >      end >    end > >    class PersonKnowsKungFu < Person >      def fight >        "5-fisted punch" >        super >      end >    end > >    class PersonMovesLikeThem < PersonKnowsKungFu >      def fight >        "bullet-time" >        super >      end >    end > >    class Neo < PersonMovesLikeThem >      def fight >        "woah" >        super >      end >    end Your last attempt via inheritance is unrealistic because the number of combinations is not manageable. You can do the math yourself. > The goal of all of these would be to easily add new skills and > characters. Obviously the class-based inheritance example is brittle > because it'd take a refactoring to give someone bullet-time without > kung-fu. I'm mostly interested in the differences between the > "leaf-class-mixins" strategy and the strategy pattern. "Prefer > composition over inheritance" would suggest you should use the strategy > pattern, but mixing in modules sure seems like a simple and ruby-ish way > of achieving the same thing. The only difference I can see is that the > strategy pattern allows for run-time skill manipulation, which I don't > find myself needing often. What other advantages would I gain from > preferring composition here? Your example reminds me more of a situation where an instance needs to be able to fill out different _roles_ that does it sound like strategy. Key point of strategy is that you have one strategy implementation at any point in time. The roles may change over time (you learn a new skill, you loose another). Any type related solution (inheritance, mixin) to your problem breaks at this point because it is static: you cannot change it at runtime (attempts have to be done to demixin a module but AFAIK there is no good solution to this). The point to understand is that a person _has_ some skills _at a particular point in time_. So your first approach augmented with some logic that let's the skill know who is performing at the moment is actually superior to the other two approaches. That way you can create an arbitrary number of Persons and give each one individual skills and change their skill set. Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/