What
Factory method design pattern is a type of creational pattern. This pattern controls the creation of objects. It exposes an interface to the user or client for creating a product.
It hides all details of product and its structure inside the interface. Therefore, the client only needs to pass parameter to the interface to select the type.
Why
In order to understand need of Factory method design pattern lets take an example. This application needs to create a Monitor Screen based on user requirement.
Currently, assume that application only supports flat and curved monitors.
Application without using Factory Method Pattern
#include<iostream> //main header
using namespace std;//for namespace
enum SCREEN_TYPE
{
FLAT_SCREEN ,
CURVED_SCREEN
} ;
class monitorScreen
{
public:
virtual void screenDispatched()=0;
};
class flatScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Flat Screen Dispatched";
}
};
class curvedScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Curved Screen Dispatched";
}
};
int main() //Client Code
{
monitorScreen *m1;
SCREEN_TYPE screenType =FLAT_SCREEN ;
if( screenType == FLAT_SCREEN)
m1 = new flatScreen;
else if( screenType == CURVED_SCREEN)
m1 = new curvedScreen;
m1->screenDispatched();
}
Please note that main function represents client code.
Now with time, the technology changes. Hence, the application needs to support more screen types. For instance, circular, rectangular, 3D, HD screens.
Therefore, the above application should adapt for new requirements. We need to update:
- Monitor Screen class structure.
- Main function code (user or client) code .
Updated example
#include<iostream> //main header
using namespace std; //for namespace
enum SCREEN_TYPE
{
FLAT_SCREEN ,
CURVED_SCREEN ,
3D_SCREEN,
HD_SCREEN
} ;
class monitorScreen
{
public:
virtual void screenDispatched()=0;
};
class flatScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Flat Screen Dispatched";
}
};
class curvedScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Curved Screen Dispatched";
}
};
class 3DScreen: public monitorScreen //New type
{
public:
void screenDispatched()
{
cout<<"3D Screen Dispatched";
}
};
class HDScreen: public monitorScreen //New type
{
public:
void screenDispatched()
{
cout<<"HD Screen Dispatched";
}
};
int main()
{
monitorScreen *m1;
SCREEN_TYPE screenType =THREED_SCREEN ;
if( screenType == FLAT_SCREEN)
m1 = new flatScreen;
else if( screenType == CURVED_SCREEN)
m1 = new curvedScreen;
else if( screenType == 3D_SCREEN) //New code
m1 = new ThreeeDScreen;
else if( screenType == HD_SCREEN) //New code
m1 = new HdScreen;
m1->screenDispatched();
}
Clearly, with the traditional approach, we need to adapt both creator class as well as user. This is because, there is tight coupling between user and creator.
Factory Method Design Pattern
Factory method design pattern is the best fit here. Because, it gives us the flexibility of enhancing the creator without much impacting client.
It give us flexibility of adopting Monitor Screen application. Therefore, it supports different monitors without impacting main function much.
How
Lets try to update Monitor Screen application with factory method pattern. Need to follow specific very simple rules for this.
1. Declare Static factory method
Declare this method inside the creator class. This method shall return object of the product class.
monitorScreen* monitorScreen::createMonitorScreen
(SCREEN_TYPE screenType);
2. Update Client or user to use factory method
Client shall create desired monitor screen using factory class. He will not instead create the monitor by himself.
monitorScreen *m1=monitorScreen::createScreen(screenType);
Monitor Screen application with Factory Method Design Pattern
#include<iostream> //main header
using namespace std;//for namespace
enum SCREEN_TYPE
{
FLAT_SCREEN ,
CURVED_SCREEN ,
3D_SCREEN,
HD_SCREEN
} ;
class monitorScreen
{
public:
//Factory Method
static monitorScreen *createMonitorScreen
(SCREEN_TYPE screenType);
virtual void screenDispatched()=0;
};
class flatScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Flat Screen Dispatched";
}
};
class curvedScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"Curved Screen Dispatched";
}
};
class 3DScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"3D Screen Dispatched";
}
};
class HDScreen: public monitorScreen
{
public:
void screenDispatched()
{
cout<<"HD Screen Dispatched";
}
};
monitorScreen* monitorScreen::createMonitorScreen
(SCREEN_TYPE screenType)
{
monitorScreen *m1 = NULL;
if( screenType == FLAT_SCREEN)
m1 = new flatScreen;
else if( screenType == CURVED_SCREEN)
m1 = new curvedScreen;
else if( screenType == THREED_SCREEN)
m1 = new ThreeeDScreen;
else if( screenType == HD_SCREEN)
m1 = new HdScreen;
return m1;
}
int main()
{
SCREEN_TYPE screenType = 3D_SCREEN ;
monitorScreen *m1 =
monitorScreen::createMonitorScreen(screenType);
m1->screenDispatched();
}
So, if in future we need to update an application for more screens. Then, we simply need to change factory method. There shall be no changes on client side application for creating screen. For instance, suppose now a new screen got added. Only need to change only the factory method and not change in main function(client code)
monitorScreen* monitorScreen::createScreen(SCREEN_TYPE screenType)
{
monitorScreen *m1 = NULL;
if( screenType == FLAT_SCREEN)
m1 = new flatScreen;
else if( screenType == CURVED_SCREEN)
m1 = new curvedScreen;
else if( screenType == THREED_SCREEN)
m1 = new ThreeeDScreen;
else if( screenType == HD_SCREEN)
m1 = new HdScreen;
else if( screenType == 5D_SCREEN) //New type
m1 = new HdScreen;
return m1;
}
Pros & Cons of Factory Method Design Pattern
Pros
- Ownership is with creator class factory method only. So, the client need not bother about creation of objects.
- Secondly, the creation of objects and it structure are completely hidden.
- Finally, the future update of the application is very easy.
Cons
- Every product class needs to have factory method.