Abstract Factory Pattern
Previously we learn JAVA - Factory Pattern
The Abstract Factory is known as a creational pattern - it's used to construct objects such that they can be decoupled from the implementing system.
The pattern is best utilized when your system has to create multiple families of products or you want to provide a library of products without exposing the implementation details. As you'll have noticed, a key characteristic is that the pattern will decouple the concrete classes from the client.
Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
The AbstractFactory class is the one that determines the actual type of the concrete object and creates it, but it returns an abstract pointer to the concrete object just created.
One of the main benefits of this pattern is that the client is totally decoupled from the concrete products. Also, new product families can be easily added into the system, by just adding in a new type of ConcreteFactory that implements AbstractFactory, and creating the specific Product implementations.
Implementing an Example:
We are going to create a Shape and Color interfaces and concrete classes implementing these interfaces. We create an abstract factory class AbstractFactory. Factory classes ShapeFactory and ColorFactory are defined where each factory extends AbstractFactory. A factory creator/generator class FactoryProducer is created.
Our demo class uses FactoryProducer to get a AbstractFactory object. It will pass information (CIRCLE / RECTANGLE / SQUARE for Shape) to AbstractFactory to get the type of object it needs. It also passes information (RED / GREEN / BLUE for Color) to AbstractFactory to get the type of object it needs.
Create an interface for Shapes.
// Shape.java
publicinterfaceShape{
void draw();
}
Create concrete classes implementing the same interface.
// Circle.java
publicclass Circle implements Shape{
@Override
publicvoid draw(){
// TODO Auto-generated method stub
System.out.println("Inside Circle: draw() method.");
}
}
// Square.java
publicclass Square implements Shape{
@Override
publicvoid draw(){
// TODO Auto-generated method stub
System.out.println("Inside Square: draw() method.");
}
}
// Rectangle.java
publicclass Rectangle implements Shape{
@Override
publicvoid draw(){
// TODO Auto-generated method stub
System.out.println("Inside Rectangle: draw() method.");
}
}
Create an interface for Colors.
// Color.java
publicinterfaceColor{
void fill();
}
Create concrete classes implementing the same interface.
// Blue.java
publicclass Blue implementsColor{
@Override
publicvoidfill() {
// TODO Auto-generated method stub
System.out.println("Inside Blue: fill() method.");
}
}
// Green.java
publicclass Green implements Color{
@Override
publicvoid fill() {
// TODO Auto-generated method stub
System.out.println("Inside Green: fill() method.");
}
}
// Red.java
publicclass Red implementsColor{
@Override
publicvoidfill() {
// TODO Auto-generated method stub
System.out.println("Inside Red: fill() method.");
}
}
Create an Abstract class to get factories for Color and Shape Objects.
// AbstractFactory
publicabstractclassAbstractFactory{
abstract Shape getShape(String shape);
abstract Color getColor(String color);
}
Create Factory classes extending AbstractFactory to generate object of concrete class based on given information.
// ShapeFactory.java
publicclass ShapeFactory extendsAbstractFactory{
@Override
Shape getShape(String shapeType) {
// TODO Auto-generated method stub
if(shapeType == null){
returnnull;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
returnnew Circle();
}elseif(shapeType.equalsIgnoreCase("RECTANGLE")){
returnnew Rectangle();
}elseif(shapeType.equalsIgnoreCase("SQUARE")){
returnnew Square();
}
returnnull;
}
@Override
Color getColor(String color) {
// TODO Auto-generated method stub
returnnull;
}
}
// ColorFactory.java
publicclass ColorFactory extends AbstractFactory{
@Override
Shape getShape(String shape) {
// TODO Auto-generated method stub
returnnull;
}
@Override
Color getColor(String color) {
// TODO Auto-generated method stub
if(color == null){
returnnull;
}
if(color.equalsIgnoreCase("RED")){
returnnew Red();
}elseif(color.equalsIgnoreCase("GREEN")){
returnnew Green();
}elseif(color.equalsIgnoreCase("BLUE")){
returnnew Blue();
}
returnnull;
}
}
Create a Factory generator/producer class to get factories by passing an information such as Shape or Color
// FactoryProducer
publicclass FactoryProducer {
publicstatic AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
returnnew ShapeFactory();
}elseif(choice.equalsIgnoreCase("COLOR")){
returnnew ColorFactory();
}
returnnull;
}
}
Use the FactoryProducer to get AbstractFactory in order to get factories of concrete classes by passing an information such as type.
// AbstractFactoryPatternDemo.java
publicclass AbstractFactoryPatternDemo {
publicstaticvoid main(String[] args){
//.......get shape factory........
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
// get an object of Shape Circle
Shape shape1 = shapeFactory.getShape("CIRCLE");
// call draw method of Shape Circle
shape1.draw();
// get an object of Shape Rectangle
Shape shape2 = shapeFactory.getShape("RECTANGLE");
// call draw method of Shape Rectangle
shape2.draw();
// get an object of Shape Square
Shape shape3 = shapeFactory.getShape("SQUARE");
// call draw method of Shape Square
shape3.draw();
//.........get color factory..........
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//get an object of Color Red
Color color1 = colorFactory.getColor("RED");
// call fill method of Red
color1.fill();
//get an object of Color Green
Color color2 = colorFactory.getColor("GREEN");
// call fill method of Green
color2.fill();
//get an object of Color Blue
Color color3 = colorFactory.getColor("BLUE");
// call fill method of Blue
color3.fill();
}
}
Verify the output.
Watch Out for the Downsides:
While the pattern does a great job of hiding implementation details from the client, there is always a chance that the underlying system will need to change. We may have new attributes to our AbstractProduct, or AbstractFactory, which would mean a change to the interface that the client was relying on, thus breaking the API.
Leave Comment