Let’s first understand what is the problem with Object creation if we do not use any kind of pattern. Once we understand there are real issues when we create an object without implying any of the object creation frameworks, it would be very easy for us to understand what is the need of a “Factory Method”.

Consider our friend Mr. Chirag is running CakeStore and he wants to open his store franchise in different cities. But one thing Mr. Chirag does not want is to compromise packaging procedure of cake created by him. Different classes and interfaces which Mr. Chirag is using to prepare various kinds of cakes are.

interface Cake {
 void prepare();

 void bake();

 void cool();

 void pack();
}

class SimpleCake implements Cake {
 String name;

 SimpleCake() {
   this.name = this.getClass().getSimpleName();
 }

 @Override
 public void prepare() {
   System.out.println("Preparing " + this.name);
 }

 @Override
 public void bake() {
   System.out.println("Baking " + this.name);
 }

 @Override
 public void cool() {
   System.out.println("Cooling " + this.name);
 }

 @Override
 public void pack() {
   System.out.println("Packing " + this.name);
 }
}

class MumbaiLayeredCheeseCake extends SimpleCake {
}

class PuneLayeredCheeseCake extends SimpleCake {

}

class MumbaiLayeredMochaCake extends SimpleCake {

}

class PuneLayeredMochaCake extends SimpleCake {

}

class MumbaiChocolateBlackOutCake extends SimpleCake {

}

class PuneChocolateBlackOutCake extends SimpleCake {

}

class MumbaiChocolateRaspberyCake extends SimpleCake {

}

class PuneChocolateRaspberyCake extends SimpleCake {

}

enum CakeType {
 ChocolateBlackOutCake,
 ChocolateRaspberyCake,
 LayeredCheeseCake,
 LayeredMochaCake
}

With above classes on table, we would write cake creation code for different franchise somewhat like this..

class ChiragCakeStore {
    public <T extends Cake> Cake orderCake(String city, CakeType cakeType) {
        Cake cake = this.createCake(city, cakeType);
        if (null != cake) {
            cake.prepare();
            cake.bake();
            cake.cool();
            cake.pack();
            return cake;
        }
        return null;
    }

    private <T extends Cake> Cake createCake(String city, CakeType cakeClass) {
        Cake cake = null;
        if (city.equalsIgnoreCase("mumbai")) {
            if (cakeClass == CakeType.ChocolateBlackOutCake) {
                cake = new MumbaiChocolateBlackOutCake();
            } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
                cake = new MumbaiChocolateRaspberyCake();
            } else if (cakeClass == CakeType.LayeredCheeseCake) {
                cake = new MumbaiLayeredCheeseCake();
            } else if (cakeClass == CakeType.LayeredMochaCake) {
                cake = new MumbaiLayeredMochaCake();
            }
        } else if (city.equalsIgnoreCase("pune")) {
            if (cakeClass == CakeType.ChocolateBlackOutCake) {
                cake = new PuneChocolateBlackOutCake();
            } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
                cake = new PuneChocolateRaspberyCake();
            } else if (cakeClass == CakeType.LayeredCheeseCake) {
                cake = new PuneLayeredCheeseCake();
            } else if (cakeClass == CakeType.LayeredMochaCake) {
                cake = new PuneLayeredMochaCake();
            }
        }
        return cake;
    }

}

 

Are you able to spot the issue ? What if Mr. Chirag has an offer on his table to open a franchise in Surat city? We would need to change our code in the createCake method to introduce new if condition for a new city and bunch of new nested if conditions in it to create new cake objects. But what if we can fix this by using Factory Method and introduce fine grained control to create concrete instances of cake based on the city in which our franchises exists without modifying our cake creation process? Wouldn’t it be cool enough?

I am sure your answer is “Yes”. So let’s do that..

First, let’s digest UML diagram of Factory Method.

 

According to above UML diagram, we must have creator, product, ConcreteCreator and ConcreteProduct right ? So let’s try to map our existing classes with this terminology and find out if we really have these things on table or not ?

If we look closely we can consider our cake Interface as Product and its all implementations as ConcreteProduct, then we can consider ChiragCakeStore as Creator but where are its ConcreteCreators. Obvisouly there aren’t any. But That’s the interesting part. We will consider each city franchise as the ConcreteCreator and then offer them a way to create their respective concrete cake objects so when new city franchise opens up, all we need to do is to create a simple child class which extends from the main Creator and it’ll have its own way of creating new cake objects.

But to achieve that level of flexibility we would need to modify our ChiragCakeStore class a bit. Here is the modified version of the ChiragCakeStore class.

abstract class ChiragCakeStore {
    public <T extends Cake> Cake orderCake(CakeType cakeType) {
        Cake cake = this.createCake(cakeType);
        if (null != cake) {
            cake.prepare();
            cake.bake();
            cake.cool();
            cake.pack();
            return cake;
        }
        return null;
    }

    abstract protected <T extends Cake> Cake createCake(CakeType cakeClass);
}

We have declared ChiragCakeStore as abstract class and keep its createCake method abstract. Now we want its child classes [ ConcreteCreators ] to return us the actual cake object.

So let’s design our Concrete Franchiese classes now. 🙂

class ChiragMumbaiCakeFactory extends ChiragCakeStore {

  @Override
  protected <T extends Cake> Cake createCake(CakeType cakeClass) {
      if (cakeClass == CakeType.ChocolateBlackOutCake) {
          return new MumbaiChocolateBlackOutCake();
      } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
          return new MumbaiChocolateRaspberyCake();
      } else if (cakeClass == CakeType.LayeredCheeseCake) {
          return new MumbaiLayeredCheeseCake();
      } else if (cakeClass == CakeType.LayeredMochaCake) {
          return new MumbaiLayeredMochaCake();
      } else {
          return null;
      }
  }
}

class ChiragPuneCakeFactory extends ChiragCakeStore {

  @Override
  protected <T extends Cake> Cake createCake(CakeType cakeClass) {
      if (cakeClass == CakeType.ChocolateBlackOutCake) {
          return new PuneChocolateBlackOutCake();
      } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
          return new PuneChocolateRaspberyCake();
      } else if (cakeClass == CakeType.LayeredCheeseCake) {
          return new PuneLayeredCheeseCake();
      } else if (cakeClass == CakeType.LayeredMochaCake) {
          return new PuneLayeredMochaCake();
      } else {
          return null;
      }
  }
}

Now let’s combile all these things into single Java file, so that we can finally summarise benifits of our these changes and Factory Method.

interface Cake {
    void prepare();

    void bake();

    void cool();

    void pack();
}

class SimpleCake implements Cake {
    String name;

    SimpleCake() {
        this.name = this.getClass().getSimpleName();
    }

    @Override
    public void prepare() {
        System.out.println("Preparing " + this.name);
    }

    @Override
    public void bake() {
        System.out.println("Baking " + this.name);
    }

    @Override
    public void cool() {
        System.out.println("Cooling " + this.name);
    }

    @Override
    public void pack() {
        System.out.println("Packing " + this.name);
    }
}

class MumbaiLayeredCheeseCake extends SimpleCake {
}

class PuneLayeredCheeseCake extends SimpleCake {

}

class MumbaiLayeredMochaCake extends SimpleCake {

}

class PuneLayeredMochaCake extends SimpleCake {

}

class MumbaiChocolateBlackOutCake extends SimpleCake {

}

class PuneChocolateBlackOutCake extends SimpleCake {

}

class MumbaiChocolateRaspberyCake extends SimpleCake {

}

class PuneChocolateRaspberyCake extends SimpleCake {

}

enum CakeType {
    ChocolateBlackOutCake,
    ChocolateRaspberyCake,
    LayeredCheeseCake,
    LayeredMochaCake
}


abstract class ChiragCakeStore {
    public <T extends Cake> Cake orderCake(CakeType cakeType) {
        Cake cake = this.createCake(cakeType);
        if (null != cake) {
            cake.prepare();
            cake.bake();
            cake.cool();
            cake.pack();
            return cake;
        }
        return null;
    }

    abstract protected <T extends Cake> Cake createCake(CakeType cakeClass);
}

class ChiragMumbaiCakeFactory extends ChiragCakeStore {

    @Override
    protected <T extends Cake> Cake createCake(CakeType cakeClass) {
        if (cakeClass == CakeType.ChocolateBlackOutCake) {
            return new MumbaiChocolateBlackOutCake();
        } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
            return new MumbaiChocolateRaspberyCake();
        } else if (cakeClass == CakeType.LayeredCheeseCake) {
            return new MumbaiLayeredCheeseCake();
        } else if (cakeClass == CakeType.LayeredMochaCake) {
            return new MumbaiLayeredMochaCake();
        } else {
            return null;
        }
    }
}

class ChiragPuneCakeFactory extends ChiragCakeStore {

    @Override
    protected <T extends Cake> Cake createCake(CakeType cakeClass) {
        if (cakeClass == CakeType.ChocolateBlackOutCake) {
            return new PuneChocolateBlackOutCake();
        } else if (cakeClass == CakeType.ChocolateRaspberyCake) {
            return new PuneChocolateRaspberyCake();
        } else if (cakeClass == CakeType.LayeredCheeseCake) {
            return new PuneLayeredCheeseCake();
        } else if (cakeClass == CakeType.LayeredMochaCake) {
            return new PuneLayeredMochaCake();
        } else {
            return null;
        }
    }
}

public class SingleFileDemo {
    public static void main(String[] args) {
        ChiragCakeStore mumbaiCakeFactory = new ChiragMumbaiCakeFactory();
        ChiragCakeStore puneCakeFactory = new ChiragPuneCakeFactory();


        mumbaiCakeFactory.orderCake(CakeType.ChocolateBlackOutCake);
        System.out.println();
        mumbaiCakeFactory.orderCake(CakeType.ChocolateRaspberyCake);
    }
}

As you can see, now we have much more flexibility in creating cake objects according to the different city franchiese. If you can see closely enough you’ll understand now if Mr. Chirag need to open new franchiese in new city, all his IT team to do would be to create child class of ChiragCakeStore and provide implementation for the createCake method.

So now as we have well understanding of Factory Method, allow me to conclude this article with its formal defination.

Define an interface for creating an object, but let subclasses decide which class to instantiate. The Factory method lets a class defer instantiation it uses to subclasses.