Teaser Image

qnoid

Markos Charatzas - Edinburgh, UK




/**
     * A lame dragon that can't make up his mind
     */
    public class IndecisiveDragon
    {
        public static final int FIRE_BREATH = 0;
        public static final int POISON_BREATH = 1;

        /*
         * Should be final but the default constructor forces us to forfeit
         */
        private int breath;
        private int damage;
        
        /**
         * Avoid naive constructor!
         * Forces breath and damage not to be final, and doesn't specify reasonable defaults.
         * Also, fails to show intend. What's a dragon without its breath?
         */
        public IndecisiveDragon(){}

        /**
         * @param breath which type of breath the dragon shall deal 
         * @param damage how much damage the breath should deal
         * @see #FIRE_BREATH
         * @see #POISON_BREATH
         */
        public IndecisiveDragon(int breath, int damage)
        {
            this.breath = breath;
            this.damage = damage;
        }
        
        /**
         * Rain chaos with just a breath  
         * @return the damage dealt by the dragon
         */
        public int breathe()
        {
            /*
             * Picture this as an if doing String comparisons and it only gets worse
             */
            switch (this.breath)
            {
            case FIRE_BREATH:
                System.out.println( String.format("Breathing mighty fire dealing %d damage", this.damage) );
            return this.damage;            
            case POISON_BREATH:
                System.out.println( String.format("Unleasing poison dealing %d damage", this.damage) );
            return this.damage;            
            default:
                System.out.println("Pants...Dealing no damage"); 
            return 0;
            }
        }
    }

    /**
     * A mighty dragon that can have custom {@link Breaths}
     */
    public class MightyDragon
    {
        /*
         * A dragon *has* breath
         */
        private final Breath breath;

        /**
         * @param breath give me breath
         * @see Breaths
         */
        public MightyDragon(Breath breath)
        {
            this.breath = breath;
        }

        /**
         * Rain chaos with just a breath  
         * @see Breath#breath()
         */
        public int breathe(){
        return this.breath.breathe();
        }
    }

    /**
     * The mighty breath
     */
    public interface Breath
    {
        /**
         * @return the damage dealt
         */
        public int breathe();
    }

    /**
     * Lot's of breaths for your perusal
     */
    public final class Breaths
    {
        /**
         * Making constants out of breaths is goooooooood.
         */
        public static final Breath FIRE_BREATH = new FireBreath(30);
        public static final Breath HEAVY_FIRE_BREATH = new HeavyFireBreath(20);
        public static final Breath POISON_BREATH = new PoisonBreath(50);
        public static final Breath NO_BREATH = new NoBreath();
    }

    final class FireBreath implements Breath
    {
        private final int damage;
        
        FireBreath(int damage)
        {
            this.damage = damage;
        }

        @Override
        public int breathe()
        {
            System.out.println( String.format("Casting mighty fire dealing %d damage", this.damage) );
        return this.damage;      
        }
    }

    final class PoisonBreath implements Breath
    {
        private final int damage;

        PoisonBreath(int damage)
        {
            this.damage = damage;
        }

        @Override
        public int breathe()
        {
            System.out.println( String.format("Unleasing poison dealing %d damage", this.damage) );
        return this.damage;      
        }
    }

    final class NoBreath implements Breath
    {
        @Override
        public int breathe()
        {
            System.out.println( "Pants...Dealing no damage" );
        return 0;      
        }
    }

    final class HeavyFireBreath implements Breath
    {
        private final int damage;
        private int multiplier;
        
        
        /**
         * @param damage
         */
        HeavyFireBreath(int damage)
        {
            this.damage = damage;
        }

        /**
         * Increments the multiplier and multiplies it to the damage
         * @param damage the damage to multiply
         * @return a multiplied damage
         */
        private int inhale(int damage) {
        return ++multiplier * damage;
        }

        @Override
        public int breathe() 
        {
            int damage = this.inhale(this.damage);
            System.out.println( String.format("Inhaling up to %d times dealing %d damage", this.multiplier, damage) );
        
        return damage;     
        }
    }


    /**
     * Here be dragons
     */
    public class MightyDragonTest
    {
        @Test
        public void breathe() throws Exception
        {
            MightyDragon harmlessDragon = new MightyDragon(Breaths.NO_BREATH);
            MightyDragon fireDragon = new MightyDragon(Breaths.FIRE_BREATH);
            MightyDragon poisonDragon = new MightyDragon(Breaths.POISON_BREATH);
            MightyDragon heavyFireDragon = new MightyDragon(Breaths.HEAVY_FIRE_BREATH);
            
            Assert.assertEquals(0, harmlessDragon.breathe());
            Assert.assertEquals(30, fireDragon.breathe());
            Assert.assertEquals(50, poisonDragon.breathe());
            Assert.assertEquals(20, heavyFireDragon.breathe());
            Assert.assertEquals(40, heavyFireDragon.breathe());
            Assert.assertEquals(60, heavyFireDragon.breathe());
        }
    }

Most of us would go mental having a dragon as a pet but what's even more fun is creating your own! Now, designing your own dragon will most likely make you over excited causing you to simply dump code.

Take a look at the IndecisiveDragon class. This class has a breath property which is merely an int acting as a flag to designate the type of breath your dragon has. Having an int for that purpose not only poses a risk for arbitary breaths (thus making it harder to debug) but makes extending the dragon with a new breath even harder. Not to mention how scary your dragon can grow. And you don't want that dragon turned against you.

In order to cope with the different types of breaths the #breathe method has a switch statement which depending on the value does something different. The important thing to notice is that a dragon has a breath therefore the #breathe method merely executes one branch per dragon (1 dragon - 1 breath, more on multiplicity later)

Finally, there is a naive default constructor which is used to just create a dragon and be done with it. Most likely this happens at a later stage when adding a method to the IndecisiveDragon class that has nothing to do with breath(s) or even the dragon itself in most cases.

Say hello to the MightyDragon. This dragon clearly states it's relationship with a Breath type which not only ensures type safety among breaths but allows you to extend your class without modifying it making it easier to choose between different breaths. Notice how you delegate the code to the breath thus in essence applying polymorphism since every breath is implemented differently.

When creating a MightyDragon you are forced to explicitly choose 1 breath which the dragon shall have. (In OO terms this is actually the Strategy design pattern)

What's also important to notice is how the HeavyFireBreath class defines an extra method related to the #breathe which otherwise would have ended up either inside your dragon class or part of the switch statement.

As a added bonus you get constants out of breaths which is gooooood.

Leave your comment below on how it helped you make your life a bit better without another if

Disclaimer: Again this is a trivial example. FireBreath, PoisonBreath and NoBreath aren't really that different since they all return the damage dealt, so a single class would suffice. In any case the breath interface allows you to extend your dragon (adding custom breaths) without modifying its internal(s) [organs].

Kudoz: Mark Shuttleworth

Source