Codiwan.com

The blog for Design Patterns, Linux, HA and Myself!

Simple Factory Pattern With Real World Example In Java

Factory Pattern or Simple Factory Design Pattern: Learn Simple Factory Design Pattern from Real World Example by creating a Milk Shake Factory

In this chapter we will look into the Simple Factory Pattern(or an Idiom?). To learn about the Simple Factory Pattern, we’ll create a simple Milk Shake Factory. Like the earlier posts we’ll be dividing this chapter into multiple stages, and we’ll progress from one stage to another by adding new concepts.

This tutorial is divided into two stages:

Note: Make sure to go through the code comments as well. It’ll help you understand the concept better.

Stage 1

In this stage we’ll be creating a simple Milk Shake shop. Our MilkShakeShop has a method MilkShakeShop.orderShake(String) that accepts a String parameter for the type of the MilkShake. Afterwards, it creates an object based on the argument and then adds the other ingredients and blends it before returning it back to the client.

package pl.hfdp.factory.simple.stage1;

import java.util.Collections;
import java.util.List;

/**
 * MilkShake is the base class for the types of the milk shakes.
 */
public abstract class MilkShake {
    /**
     * ingredients is list of the items that'll be used for preparing the milk shake.
     */
    List<String> ingredients = Collections.singletonList("Milk");

    /**
     * addMilk starts the preparation by adding the milk
     */
    public void addMilk() {
        System.out.println("Adding the milk...");
    }

    /**
     * addIngredients is the next step after adding the milk.
     */
    public void addIngredients() {
        System.out.println("Adding the ingredients... " + ingredients.toString());
    }

    /**
     * blends the shake. Our final step.
     */
    public void blend() {
        System.out.println("Blending... " + ingredients.toString());
    }

    public String toString() {
        return "MilkShake{" +
                "ingredients=" + ingredients +
                '}';
    }
}

/**
 * Chocolate Milk Shake is a type of Milk Shake
 */
public class ChocolateMilkShake extends MilkShake {

    /**
     * Adds Chocolate to the MilkShake
     */
    public ChocolateMilkShake() {
        this.ingredients = Arrays.asList("Milk", "Chocolate");
    }

    /**
     * @return name of the Shake that this class is preparing
     */
    public static String name() {
        return "Chocolate Milk Shake";
    }
}

/**
 * Banana Milk Shake is type of a Milk Shake.
 */
public class BananaMilkShake extends MilkShake {

    /**
     * Add banana to the ingredients
     */
    public BananaMilkShake() {
        this.ingredients = Arrays.asList("Milk", "Banana");
    }

    /**
     * @return name of the Shake that this class is preparing
     */
    public static String name() {
        return "Banana Milk Shake";
    }
}

/**
 * Strawberry Milk Shake is a type of Milk Shake
 */
public class StrawBerryMilkShake extends MilkShake {

    /**
     * Adds Strawberry to the MilkShake
     */
    public StrawBerryMilkShake() {
        this.ingredients = Arrays.asList("Milk", "Straw Berry");
    }

    /**
     * @return name of the Shake that this class is preparing
     */
    public static String name() {
        return "Strawberry Milk Shake";
    }
}

/**
 * The Milk Shake Shop takes orders for the Milk Shakes and returns the exciting Milk Shakes
 * after adding milk and other ingredients and blending it
 */
class MilkShakeShop {

    /**
     * @param type the type of the milk shake
     * @return the prepared Milk Shake
     * @throws RuntimeException if the an unidentified type is passed in.
     */
    MilkShake orderShake(String type) {
        MilkShake milkShake;

        if (StrawBerryMilkShake.name().equals(type)) {
            milkShake = new StrawBerryMilkShake();
        } else if (ChocolateMilkShake.name().equals(type)) {
            milkShake = new ChocolateMilkShake();
        } else if (BananaMilkShake.name().equals(type)) {
            milkShake = new BananaMilkShake();
        } else {
            throw new RuntimeException("the passed milk shake type is not available with us.");
        }

        milkShake.addMilk();
        milkShake.addIngredients();
        milkShake.blend();

        return milkShake;
    }
}
Stage 2

Well, our MilkShake Shop is a hit now. It has been making our customers happy. And to keep them happy we’ll be adding more variety by adding Almond Shake and Cauliflower Shake 🥴.

           if (StrawBerryMilkShake.name().equals(type)) {
               milkShake = new StrawBerryMilkShake();
           } else if (ChocolateMilkShake.name().equals(type)) {
               milkShake = new ChocolateMilkShake();
           } else if (BananaMilkShake.name().equals(type)) {
               milkShake = new BananaMilkShake();
           } else if (AlmondShake.name().equals(type)) {
               milkShake = new AlmondShake();
           } else if (CauliflowerShake.name().equals(type)) {
               milkShake = new CauliflowerShake();
           } else {
               throw new RuntimeException("the passed milk shake type is not available with us.");
           }

This way we’re adding more if/else branches. Isn’t the code looking messed up?

Also, accommodating this change related to concrete classes is keeping it open for modification and this what we don’t want to achieve.

This solution is breaking the following principles:

What we can do, right now, is to delegate the task of concrete object creation to a new Class. As the sole purpose of this class is to create objects, we’ll call it a Factory, MilkShakeFactory. This way we’ve made the MilkShakeShop.orderShake(String) closed for modification. And we’ve learnt Simple Factory Idiom(yes, this is not a Design Pattern).

/**
 * The same shop that we created in the previous stage.
 */
class MilkShakeShop {

    /**
     * This object will take care of the concrete object generation.
     */
    private MilkShakeFactory milkShakeFactory;

    MilkShakeShop(MilkShakeFactory milkShakeFactory) {
        this.milkShakeFactory = milkShakeFactory;
    }

    /**
     * @param type the type of the shake
     * @return return the delicious Milk Shake
     */
    MilkShake orderShake(String type) {
        MilkShake milkShake;

        milkShake = milkShakeFactory.createMilkShake(type);

        milkShake.addMilk();
        milkShake.addIngredients();
        milkShake.blend();

        return milkShake;
    }
}

/**
 * Out factory for creating the milk shake objects
 */
class MilkShakeFactory {
    /**
     * @param type the type of the milk shake
     * @return the concrete object for the milk shake
     */
    MilkShake createMilkShake(String type) {
        MilkShake milkShake;
        if (StrawBerryMilkShake.name().equals(type)) {
            milkShake = new StrawBerryMilkShake();
        } else if (ChocolateMilkShake.name().equals(type)) {
            milkShake = new ChocolateMilkShake();
        } else if (BananaMilkShake.name().equals(type)) {
            milkShake = new BananaMilkShake();
        } else {
            throw new RuntimeException("the passed milk shake type is not available with us.");
        }
        return milkShake;
    }
}

I’ve created these tutorials after learning Design Patterns from this book Head First Design Patterns (A Brain Friendly Guide).

Loading Comments... Disqus Loader
comments powered by Disqus