Teaser Image

qnoid

Markos Charatzas - London, UK




/**
     * Survival Kit #1
     * Rock some awesome Forrst schwag and show your support for the community.
     */
    public class SurvivalKit
    {
        /**
         * Use to construct a new survival kit.
         * 
         * The most popular size of a survival kit has been the large so far.
         * So to speed up the process the default size will be {@link Size#LARGE}
         * You can chance it if you want, don't worry.
         */
        public static final class SurvivalKitBuilder
        {
            public enum Size
            {
                TWO_XL, XL, LARGE, MEDIUM, SMALL;
            }

            /**
             * Reasonable constant
             */
            private static final int COST = 29;

            /*
             * Use the most popular size as the default one
             */
            private Size size = Size.LARGE; 

            public SurvivalKitBuilder size(Size size)
            {
                this.size = size;
            return this;
            }

            /**
             * What you get
             * 
             * An awesome cotton twshirt to wear & make your friends jealous
             *  
             *  A coffee & tea mug for enjoying a delicious beverage while 
             *  browsing the site
             *  
             *  A wristband for those intense games of curling
             *  
             *  Two awesome vinyl stickers from our friends at Sticker Giant
             * 
             * @return a new {@link SurvivalKit}
             */
            public SurvivalKit build() 
            {
                TShirt tShirt = new TShirt(this.size, Material.COTTON);
                Mug mug = new Mug();
                Wristband wristband = new Wristband();
                List<Sticker> stickers = Collections.nCopies(2, new Sticker(Material.VINYL));
                
                /*
                 * No disrespect for Joshua Bloch (thanks for the signed copy of
                 * effective java!) on his take on Builders, but I believe that 
                 * listing dependencies explicitly on the constructor allows for 
                 * greater flexibility and is more straightforward.
                 * 
                 * You are effectively hiding the constructor anyway from your 
                 * client code and you are actually calling the constructor 
                 * on a single place.
                 * 
                 * Joshua you can have an argument with Misko Hevery next door 
                 * if you don't take my argument or even the Greek goddess Demeter:P
                 */
            return new SurvivalKit(tShirt, mug, wristband, stickers, COST);
            }
        }
          
        private final TShirt tShirt;
        private final Mug mug;
        private final Wristband wristband;
        private final Collection<Sticker> stickers;
        private final int cost;
        
        /**
         * Leveraging private to package level access so as to make it easier 
         * to test different implementations and/or related functionality 
         * on the survival kit.
         * 
         * Just make sure your test is on the same package! :D 
         * 
         * @param tShirt
         * @param mug
         * @param wristband
         * @param stickers
         * @param cost
         */
        SurvivalKit(TShirt tShirt, Mug mug, Wristband wristband, Collection<Sticker> stickers, int cost)
        {
            this.tShirt = tShirt;
            this.mug = mug;
            this.wristband = wristband;
            this.stickers = stickers;
            this.cost = cost;
        }

        /**
         * @return the tShirt
         */
        public TShirt tShirt() {
        return tShirt;
        }

        /**
         * @return the mug
         */
        public Mug mug() {
        return mug;
        }

        /**
         * @return the wristband
         */
        public Wristband wristband() {
        return wristband;
        }

        /**
         * @return the stickers
         */
        public Collection<Sticker> stickers() {
        return stickers;
        }
        
        /**
         * @return the size
         */
        public Size size(){
        return this.tShirt.size();
        }
        
        public int cost(){
        return this.cost;
        }
    }

    /**
     * Making sure a survival kit doesn't miss anything.
     */
    public class SurvivalKitTest
    {
        @Test
        public void newSurvivalKit() throws Exception
        {
            SurvivalKitBuilder survivalKitBuilder = new SurvivalKitBuilder();
            SurvivalKit survivalKit = survivalKitBuilder.build();
            
            Assert.assertNotNull(survivalKit.tShirt());
            Assert.assertEquals(Size.LARGE, survivalKit.size());
            Assert.assertNotNull(survivalKit.mug());
            Assert.assertNotNull(survivalKit.stickers());
            Assert.assertEquals(2, survivalKit.stickers().size());
        }
    }

When first identified the SurvivalKit we specified all the properties that make it up.

Now that's a lot of properties! (@kyle made sure a survival kit has everything we need) which means that every time we need to add a new survival kit to an order we have to create everything that comes with it. Not only that but there is a risk of mistyping the cost, which pottentialy could make the kit more expensive, or cheaper than what it's actually worth. Not fun.

So you could go on creating survival kits; every time you need to dispatch one specifying the items that comprise it or you could use a builder.

The great thing about the #build method is that it can encapsulate the complexity of creating a SurvivalKit. The client code doesn't need to know how to go on creating a TShirt or a Mug. All is interested in is a survival kit and that's what it gets!

The #build method also provides a place to return objects in the required state while any "setter" method in the builder can be used to perform assertions on the property to set. So in essence a builder helps you create immutable objects since once the object is "built" you can't change it. (notice how the setters are missing from the SurvivalKit)

As an extra bonus you get to specify constant values for any of your properties. (e.g. the cost!)

Make sure that you document your builder on any default values it might use as well as the state that the object is returned in.

Kudoz: Joshua Bloch in Effective Java and his take on the builder pattern.

Kudoz: Misko Hevery for his great blog on testability.

Up next we'll see how a factory can help you produce different variations of a SurvivalKit and finally see the real reason behind the need for a SurvivalKitItem. Can you guess?

Disclaimer: The builder specified above is great if you have a single "configuration" of survival kits and that's usually the case. We'll see why the builder is complimentary to the factory and not a replacement altogether.