Abstract Factory Pattern

Abstract Factory pattern is one of the Creational design patterns and is the super factory of factory classes.  Because of this behavior, this patter is also called as factory of factories. This pattern provides an interface for creating factories of related or dependent objects without specifying their concrete classes. 

This pattern further makes it possible of creating specific objects from any of the concrete factory classes at run time. Parent class (super class) could be abstract class or interface. 

Benefits:

•This pattern hides the creational logic of super & sub factories from client.

•This pattern can be used in multi-level object creation.

Let us learn from an example. 

Example:

Suppose a customer wants a customized ice cream with a cone of choice, flavor of choice and topping of choice.  All choices will have an ingredient information as a common thing.  Also, every choice corresponds to a factory which in detail contains multiple options (objects).

Abstract factory is responsible for creating various top level factory objects such as IceCreamFactory, ConeFactory and ToppingFactory.

1.Creating ingredient information of various flavors of ice creams is the responsibility of IceCreamFactory.

2.Creating ingredient information of different types of cones is the responsibility of ConeFactory.

3.Creating ingredient information of different types of toppings is the responsibility of ToppingFactory.

Example Diagram:

Creating IngredientInformation Interface:

This interface is an abstract representation of all ingredients of Ice Cream making. This is the core class which would be returned by all sub-factories such as IceCreamFactory. This interface will be implemented by all classes of sub-factory objects.

package com.learningupskills.designpattern.abstractfactory;

public interface IngredientInformation {
	
	String getName();

	int getPrice();

	String getDetails();

}

Creating AbstractFactory Interface:

AbstractFactory is the top level interface which will be implemented by all the sub-factories such as IceCreamFactory and returns IngredientInformation.

package com.learningupskills.designpattern.abstractfactory;

public interface AbstractFactory{
	IngredientInformation getIncredientInformation(String itemType) throws Exception;
}

Creating IceCreamFactory:

It is a sub-factory and implements AbstractFactory interface. Creating ingredient information of various flavors of ice creams is the responsibility of IceCreamFactory. This factory creates FaloodaIceCream, KulfiIceCream and SorbetIceCream.

If the ice-cream name given is not valid or null then NoIceCreamFound exception will be thrown.

IceCreamFactory.java

package com.learningupskills.designpattern.abstractfactory.icecream;

import com.learningupskills.designpattern.abstractfactory.AbstractFactory;
import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

public class IceCreamFactory implements AbstractFactory{

	@Override
	public IngredientInformation getIncredientInformation(String itemType) throws Exception {
		if (itemType == null) {
			throw new NoIceCreamFound("Ice is not mentioned");
		}
		if (itemType.equalsIgnoreCase(IceCreamType.FALOODA.toString())) {
			return new FaloodaIceCream();
		} else if (itemType.equalsIgnoreCase(IceCreamType.KULFI.toString())) {
			return new KulfiIceCream();
		} else if (itemType.equalsIgnoreCase(IceCreamType.SORBET.toString())) {
			return new SorbetIceCream();
		} else {
			throw new NoIceCreamFound(itemType.toString() +" : IceCreame is not available");
		}
	}	

}

IceCreamType.java

package com.learningupskills.designpattern.abstractfactory.icecream;

public enum IceCreamType {
	KULFI,
	SORBET,
	FALOODA
}

FaloodaIceCream.java

package com.learningupskills.designpattern.abstractfactory.icecream;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class FaloodaIceCream implements  IngredientInformation{


	@Override
	public String getName() {
		return IceCreamType.FALOODA.toString();
	}

	@Override
	public String getDetails() {
		return "Falooda is an Indian version of a cold dessert that has its origin in Persian cuisine";
	}

	@Override
	public int getPrice() {
		return 65;
	}

}

KulfiIceCream.java

package com.learningupskills.designpattern.abstractfactory.icecream;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class KulfiIceCream implements  IngredientInformation {

	
	@Override
	public String getName() {
	  return IceCreamType.KULFI.toString();
	}

	@Override
	public String getDetails() {
	 return "This traditional Indian ice cream is made by slowly heating sweetened milk until the sugar caramelizes and the milk condense";
	}

	@Override
	public int getPrice() {
	 return 90;
	}

}

SorbetIceCream.java

package com.learningupskills.designpattern.abstractfactory.icecream;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class SorbetIceCream implements IngredientInformation {

	@Override
	public String getName() {
		return IceCreamType.SORBET.toString();

	}

	@Override
	public String getDetails() {
		return "Sorbet is a fruit-based frozen dessert that contains dairy and no more than three percent milk fat";
	}

	@Override
	public int getPrice() {
		return 75;
	}

}

NoIceCreamFound.java

package com.learningupskills.designpattern.abstractfactory.icecream;

 class NoIceCreamFound extends Exception {

	private static final long serialVersionUID = 1L;

	public NoIceCreamFound(String str) {
		super(str);
	}

}

Creating ConeFactory:

It is a sub-factory and implements AbstractFactory interface. Creating ingredient information of various cone types is the responsibility of ConeFactory. This factory creates CakeCone, SugarCone and WaffleCone.

If the cone name given is not valid or null then NoConeFound exception will be thrown.

ConeFactory.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

import com.learningupskills.designpattern.abstractfactory.AbstractFactory;
import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

public class ConeFactory implements AbstractFactory {

	@Override
	public IngredientInformation getIncredientInformation(String itemType) throws Exception {
		if (itemType == null) {
			throw new NoConeFound("Cone is not mentioned");
		}
		if (itemType.equalsIgnoreCase(ConeType.SUGAR.toString())) {
			return new SugarCone();
		} else if (itemType.equalsIgnoreCase(ConeType.CAKE.toString())) {
			return new CakeCone();
		} else if (itemType.equalsIgnoreCase(ConeType.WAFFLE.toString())) {
			return new WaffleCone();
		} else {
			throw new NoConeFound(itemType.toString() + " : IceCreameCone is not available");
		}
	}

}

ConeType.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

public enum ConeType {
	SUGAR,
	WAFFLE,
	CAKE
}

CakeCone.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class CakeCone implements IngredientInformation{

	
	@Override
	public String getName() {
		return ConeType.CAKE.toString();
	}

	@Override
	public int getPrice() {
		return 15;
	}

	@Override
	public String getDetails() {
		return "This is made of cake";
		
	}

}

SugarCone.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class SugarCone implements IngredientInformation {

	@Override
	public String getName() {
		return ConeType.SUGAR.toString();
	}

	@Override
	public int getPrice() {
		return 10;
	}

	@Override
	public String getDetails() {
		return "This is made of sugar based";
	}

}

WaffleCone.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class WaffleCone implements IngredientInformation {

	@Override
	public String getName() {
		return ConeType.WAFFLE.toString();
	}

	@Override
	public int getPrice() {
		return 20;
	}

	@Override
	public String getDetails() {
		return "This is made of chocolate";
	}

}

NoConeFound.java

package com.learningupskills.designpattern.abstractfactory.icecreamcone;

 class NoConeFound extends Exception {

	private static final long serialVersionUID = 1L;

	 NoConeFound(String str) {
		super(str);
	}

}

Creating ToppingFactory:

It is a sub-factory and implements AbstractFactory interface. Creating ingredient information of various toppings is the responsibility of ToppingFactory. This factory creates NutsTopping, FruitsTopping and MixedTopping.

If the topping name given is not valid or null then NoToppingFound exception will be thrown.

ToppingFactory.java

package com.learningupskills.designpattern.abstractfactory.topping;

import com.learningupskills.designpattern.abstractfactory.AbstractFactory;
import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

public class ToppingFactory implements AbstractFactory {

	@Override
	public IngredientInformation getIncredientInformation(String itemType) throws Exception {
		if (itemType == null) {
			throw new NoToppingFound("Topping is not mentioned");
		}
		if (itemType.equalsIgnoreCase(ToppingName.FRUITS.toString())) {
			return new FruitsTopping();
		} else if (itemType.equalsIgnoreCase(ToppingName.MIXED.toString())) {
			return new MixedTopping();
		} else if (itemType.equalsIgnoreCase(ToppingName.NUTS.toString())) {
			return new NutsTopping();
		} else {
			throw new NoToppingFound(itemType.toString() + " : Topping is not available");
		}
	}

}

ToppingName.java

package com.learningupskills.designpattern.abstractfactory.topping;

public enum ToppingName {
   FRUITS,
   NUTS,
   MIXED
}

NutsTopping.java

package com.learningupskills.designpattern.abstractfactory.topping;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

 class NutsTopping implements IngredientInformation{

	

	@Override
	public String getName() {
		return ToppingName.NUTS.toString();

	}

	@Override
	public int getPrice() {
		return 25;
	}

	@Override
	public String getDetails() {
		StringBuilder builder = new StringBuilder();
		builder.append("DryCoconut ,");
		builder.append("pistachio ,");
		builder.append("cashewnut ,");
		builder.append("almond");
		return builder.toString();
	}

}

FruitsTopping.java

package com.learningupskills.designpattern.abstractfactory.topping;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

 class FruitsTopping  implements IngredientInformation {


	@Override
	public String getName() {
		return ToppingName.FRUITS.toString();
	}

	@Override
	public int getPrice() {
		return 30;
	}

	@Override
	public String getDetails() {
		StringBuilder builder = new StringBuilder();
		builder.append("grapes ,");
		builder.append("strawberries ,");
		builder.append("plums ,");
		builder.append("mango");
		return builder.toString();
	}

}

MixedTopping.java

package com.learningupskills.designpattern.abstractfactory.topping;

import com.learningupskills.designpattern.abstractfactory.IngredientInformation;

class MixedTopping implements IngredientInformation {

	@Override
	public String getName() {
		return ToppingName.MIXED.toString();

	}

	@Override
	public int getPrice() {
		return 40;
	}

	@Override
	public String getDetails() {
		StringBuilder builder = new StringBuilder();
		builder.append("grapes ,");
		builder.append("strawberries ,");
		builder.append("cashewnut ,");
		builder.append("almond");
		return builder.toString();

	}

}

NoToppingFound.java

package com.learningupskills.designpattern.abstractfactory.topping;

  class NoToppingFound extends Exception {

	private static final long serialVersionUID = 1L;

	public NoToppingFound(String str) {
		super(str);
	}

}

Creating FactoryProvider:

FactoryProvider is the super factory which is responsible for creating all sub-factories. It creates IceCreamFactory, ConeFactory and ToppingFactory.

FactoryProvider.java

package com.learningupskills.designpattern.abstractfactory;

import com.learningupskills.designpattern.abstractfactory.icecream.IceCreamFactory;
import com.learningupskills.designpattern.abstractfactory.icecreamcone.ConeFactory;
import com.learningupskills.designpattern.abstractfactory.topping.ToppingFactory;

class FactoryProvider{
	

	public static AbstractFactory getFactory(IngredientType type) {

		if (type == IngredientType.ICE_CREAM) {
			return new IceCreamFactory();

		} else if (type == IngredientType.CONE) {
			return new ConeFactory();
		} else if (type == IngredientType.TOPPING) {
			return new ToppingFactory();
		}

		return null;
	}
}

IngredientType.java

package com.learningupskills.designpattern.abstractfactory;

 enum IngredientType {
	ICE_CREAM,
	TOPPING,
	CONE
}

Creating IceCreamBuilder:

IceCreamBuilder is responsible for getting order from customers and the client will communicate with this class. This will build the ice cream with all ingredients mentioned by customer (client).

IceCreamBuilder.java

package com.learningupskills.designpattern.abstractfactory;

public class IceCreamBuilder {

	private String iceCream;
	private String topping;
	private String cone;
	private StringBuilder build = new StringBuilder();
	private int totalPrice;
	private boolean orderStatus = true;

	public IceCreamBuilder(String iceCream, String topping, String cone) {
		this.iceCream = iceCream;
		this.cone = cone;
		this.topping = topping;
	}

	private void buildIceCream() {
		AbstractFactory iceCreamFactory = FactoryProvider.getFactory(IngredientType.ICE_CREAM);
		try {
			IngredientInformation incredientInformation = iceCreamFactory.getIncredientInformation(iceCream);
			build.append("Ice Cream Name :" + incredientInformation.getName());
			build.append(System.lineSeparator());
			build.append("Ice flavour :" + incredientInformation.getDetails());
			build.append(System.lineSeparator());
			build.append("Ice price :" + incredientInformation.getPrice());
			totalPrice = totalPrice + incredientInformation.getPrice();
			build.append(System.lineSeparator());

		} catch (Exception e) {
			orderStatus = false;
			build.append(e.getMessage());
			build.append(System.lineSeparator());
		}
	}

	private void buildIceCreamCone() {
		AbstractFactory conefactory = FactoryProvider.getFactory(IngredientType.CONE);
		try {
			IngredientInformation incredientInformation = conefactory.getIncredientInformation(cone);
			build.append("Cone Name :" + incredientInformation.getName());
			build.append(System.lineSeparator());
			build.append("Cone Description :" + incredientInformation.getDetails());
			build.append(System.lineSeparator());
			build.append("Cone price :" + incredientInformation.getPrice());
			totalPrice = totalPrice + incredientInformation.getPrice();
			build.append(System.lineSeparator());

		} catch (Exception e) {
			orderStatus = false;
			build.append(e.getMessage());
			build.append(System.lineSeparator());
		}

	}

	private void buildTopping() {
		AbstractFactory toppingFactory = FactoryProvider.getFactory(IngredientType.TOPPING);
		try {
			IngredientInformation incredientInformation = toppingFactory.getIncredientInformation(topping);

			build.append("Topping Name :" + incredientInformation.getName());
			build.append(System.lineSeparator());
			build.append("Topping added :" + incredientInformation.getDetails());
			build.append(System.lineSeparator());
			build.append("Topping price :" + incredientInformation.getPrice());
			totalPrice = totalPrice + incredientInformation.getPrice();
			build.append(System.lineSeparator());

		} catch (Exception e) {
			orderStatus = false;
			build.append(e.getMessage());
			build.append(System.lineSeparator());
		}

	}

	public String getOrderDetails() {
		buildIceCreamCone();
		buildIceCream();
		buildTopping();
		build.append("Total Price :" + totalPrice);
		build.append(System.lineSeparator());
		build.append("Order Status:" + orderStatus);
		return build.toString();
	}

}

Customer.java

package com.learningupskills.designpattern.abstractfactory.client;

import com.learningupskills.designpattern.abstractfactory.IceCreamBuilder;

public class Customer {
	
	public static void main(String[] args) {
		IceCreamBuilder sorbetIceCream = new IceCreamBuilder("SORBET", "NUTS", "SUGAR");
		System.out.println("****************************************************");
		System.out.println(sorbetIceCream.getOrderDetails());
		System.out.println("****************************************************");
		IceCreamBuilder venilaIceCream = new IceCreamBuilder("VENILA", "FRUITS", "CAKE");
		System.out.println(venilaIceCream.getOrderDetails());
		System.out.println("****************************************************");
	}

}

Result


Cone Name :SUGAR
Cone Description :This is made of sugar based
Cone price :10
Ice Cream Name :SORBET
Ice flavour :Sorbet is a fruit-based frozen dessert that contains dairy and no more than three percent milk fat
Ice price :75
Topping Name :NUTS
Topping added :DryCoconut ,pistachio ,cashewnut ,almond
Topping price :25
Total Price :110
Order Status:true


Cone Name :CAKE
Cone Description :This is made of cake
Cone price :15
VENILA : IceCreame is not available
Topping Name :FRUITS
Topping added :grapes ,strawberries ,plums ,mango
Topping price :30
Total Price :45
Order Status:false

Scroll to top