Factory pattern is one of the most used pattern in Object Oriented Design. In this post, i will present basic concepts about Simple Factory Pattern, Factory Method Pattern and Abstract Factory Pattern. Then i will show the differences among them and when we use each of them.
1. Problem description
Imagine that you was assigned to develop an application to sell mobile phones. At first, our shop sell brand new Apple phones and brand new Samsung phones only. We create a Phone abstract class inherited by concrete classes. Name and pack methods inply the phone name and the phone is packed respectively. A client (main function) create phone based on user's input.
Class diagram for the example
The C++ code for the above description can be given as follows.
- #include <iostream>
- #include <string>
- using namespace std;
- class Phone {
- public:
- virtual void name() = 0;
- void pack() {
- cout << "This phone is packed and sent to you soon" << endl;
- }
- };
- class Samsung : public Phone {
- public:
- void name() {
- cout << "This is a brand new Samsung phone" << endl;
- }
- };
- class Apple : public Phone {
- public:
- void name() {
- cout << "This is a brand new Iphone" << endl;
- }
- };
- /* Client code */
- int main() {
- string userInput = "samsung"; //simulate user input
- Phone *phone;
- if (userInput == "samsung") {
- phone = new Samsung;
- } else {
- phone = new Apple;
- }
- phone ->name();
- phone ->pack();
- return 0;
- }
"This is a brand new Samsung phone
This phone is packed and sent to you soon".
In small and simple problem, this design work fine. But later, the shop decides to sell HTC phone. We have to modify client code (User interface). This breaks the Open Closed Principle, which says software entities (class, function, ...) should be open for extension and closed for modification).
Lesson learnt, changes are obvious part of software development, but system should be developed to adapted with changes without modifying the tested sections.
2. Simple Factory Pattern.
We encapsulate the code sections should be changed (because of new phone type addition). So this modification does not affect the tested part of our software. To do that, we create a Simple Factory class to make a new Phone. Client UI code is unchanged even if new products are added.
Class diagram of simple factory pattern in theory
Class diagram of simple factory pattern for the example
The C++ code for this design is given as follows.
- #include <iostream>
- #include <string>
- using namespace std;
- class Phone {
- public:
- virtual void name() = 0;
- void pack() {
- cout << "This phone is packed and sent to you soon" << endl;
- }
- };
- class Samsung : public Phone {
- public:
- void name() {
- cout << "This is a brand new Samsung phone" << endl;
- }
- };
- class Apple : public Phone {
- public:
- void name() {
- cout << "This is a brand new Iphone" << endl;
- }
- };
- class PhoneFactory {
- public:
- Phone* makePhone(string userInput) {
- Phone *phone;
- if (userInput == "samsung") {
- phone = new Samsung;
- } else {
- phone = new Apple;
- }
- phone ->name();
- phone ->pack();
- return phone;
- }
- };
- int main() {
- string userInput = "samsung"; /*simulate user input*/
- PhoneFactory *phoneFactory = new PhoneFactory;
- Phone *phone = phoneFactory->makePhone(userInput);
- return 0;
- }
- class PhoneFactory {
- public:
- Phone* makePhone(string userInput) {
- Phone *phone;
- if (userInput == "samsung") {
- phone = new Samsung;
- } else if(userInput == "apple") {
- phone = new Apple;
- } else if(userInput == "like new samsung){
- phone = new LikeNewSamsung;
- } else {
- phone = new LikeNewApple;
- }
- phone ->name();
- phone ->pack();
- return phone;
- }
- };
- When others brand new phones are added
- When others like new phones are added
- class BrandNewPhoneFactory {
- public:
- Phone* makePhone(string userInput) {
- Phone *phone;
- if (userInput == "samsung") {
- phone = new Samsung;
- } else {
- phone = new Apple;
- }
- phone ->name();
- phone ->pack();
- return phone;
- }
- };
- class LikeNewPhoneFactory{
- Phone *phone;
- if (userInput == "samsung") {
- phone = new LikeNewSamsung;
- } else {
- phone = new LikeNewApple;
- }
- phone ->name();
- phone ->pack();
- return phone;
- }
- };
This seems to work, but the two factory classes are independent. We cannot make sure every factory class follow the same rule.
3. Factory method pattern
To solve this problem, we use the factory method pattern, which let the abstract factory class encapsulate common functionalities and the derived classes will override the abstract method.
Class diagram of factory method pattern in theory
Class diagram of factory method pattern for the example
- #include <iostream>
- #include <string>
- using namespace std;
- class Phone {
- public:
- virtual void name() = 0;
- void pack() {
- cout << "This phone is packed and sent to you soon" << endl;
- }
- };
- class Samsung : public Phone {
- public:
- void name() {
- cout << "This is a brand new Samsung phone" << endl;
- }
- };
- class Apple : public Phone {
- public:
- void name() {
- cout << "This is a brand new Iphone" << endl;
- }
- };
- class LikeNewSamsung: public Phone {
- public:
- void name() {
- cout << "This is a like new Samsung phone"<< endl;
- }
- };
- class LikeNewApple: public Phone {
- public:
- void name() {
- cout << "This is a like new Apple phone"<< endl;
- }
- };
- class PhoneFactory {
- public:
- Phone *makePhone(string userInput) {
- Phone *phone = this -> getPhone(userInput);
- phone->name();
- phone->pack();
- return phone;
- }
- virtual Phone *getPhone(string userInput) = 0;
- };
- class BrandNewPhoneFactory : public PhoneFactory{
- public:
- Phone *getPhone(string userInput) {
- if (userInput == "samsung") {
- return new Samsung;
- } else {
- return new Apple;
- }
- }
- };
- class LikeNewPhoneFactory : public PhoneFactory{
- public:
- Phone *getPhone(string userInput) {
- if (userInput == "samsung") {
- return new LikeNewSamsung;
- } else {
- return new LikeNewApple;
- }
- }
- };
- int main() {
- string userInput = "samsung"; /*simulate user input*/
- PhoneFactory *phoneFactory = new LikeNewPhoneFactory;
- Phone *phone = phoneFactory->makePhone(userInput);
- return 0;
- }
4. Factory Method
Different from simple factory and factory method, which create one product at a time, factory method pattern is used when we want to create a family of components (usually to make a product by them). For example, we have to develop a software to assemble phones based on requirements of customers. For simplicity, we assume that phones are made from two main components Board and Monitor. We have three phone types:
- Expensive phone: Created from expensive board and expensive monitor
- Medium phone: Created from expensive board and cheap monitor
- Cheap phone: Created from cheap board and cheap monitor
Class diagram of Abstract Factory Pattern in theory
Class diagram of Abstract Factory Pattern for the example
The C++ code for this example is given as follows.
- #include <iostream>
- #include <string>
- using namespace std;
- class Board {
- public:
- virtual void showBoard() = 0;
- };
- class ExpensiveBoard : public Board {
- public:
- void showBoard() {
- cout << "this is an expensive board" << endl;
- }
- };
- class CheapBoard : public Board {
- public:
- void showBoard() {
- cout << "This is a cheap board" << endl;
- }
- };
- class Monitor {
- public:
- virtual void showMonitor() = 0;
- };
- class ExpensiveMonitor : public Monitor {
- public:
- void showMonitor() {
- cout << "This is an expensive monitor" << endl;
- }
- };
- class CheapMonitor : public Monitor {
- public:
- void showMonitor() {
- cout << "This is a cheap monitor" << endl;
- }
- };
- class AbstractFactory {
- public:
- virtual Board *makeBoard() = 0;
- virtual Monitor *makeMonitor() = 0;
- };
- class ExpensivePhoneFactory : public AbstractFactory {
- public:
- Board *makeBoard() {
- return new ExpensiveBoard;
- }
- Monitor *makeMonitor() {
- return new ExpensiveMonitor;
- }
- };
- class MediumPhoneFactory : public AbstractFactory {
- public:
- Board *makeBoard() {
- return new CheapBoard;
- }
- Monitor *makeMonitor() {
- return new ExpensiveMonitor;
- }
- };
- class CheapPhoneFactory : public AbstractFactory {
- public:
- Board *makeBoard() {
- return new ExpensiveBoard;
- }
- Monitor *makeMonitor() {
- return new ExpensiveMonitor;
- }
- };
- class PhoneAssembler {
- private:
- AbstractFactory *factoryType;
- public:
- typedef enum {
- EXPENSIVE_PHONE,
- MEDIUM_PHONE,
- CHEAP_PHONE
- } PHONE_TYPE;
- PhoneAssembler(PHONE_TYPE type) {
- if (type == EXPENSIVE_PHONE) {
- factoryType = new ExpensivePhoneFactory;
- } else if (type == MEDIUM_PHONE) {
- factoryType = new MediumPhoneFactory;
- } else {
- factoryType = new CheapPhoneFactory;
- }
- }
- void assemble() {
- Board *board = factoryType->makeBoard();
- Monitor *monitor = factoryType->makeMonitor();
- board->showBoard();
- monitor->showMonitor();
- cout << "The phone is assembled" << endl;
- }
- };
- int main() {
- PhoneAssembler::PHONE_TYPE userInput = PhoneAssembler::EXPENSIVE_PHONE; /*simulate user input*/
- PhoneAssembler *phoneAssembler = new PhoneAssembler(userInput);
- phoneAssembler->assemble();
- return 0;
- }
5. Conclusion
We have learnt Simple Factory, Factory Method and Abstract Factory patterns. When we use each of them.
- Simple factory pattern: when we have only one family of objects (E.g. create one phone from brand new phone family)
- Factory method pattern: When we have multiple families of objects (E.g. create one phone from brand new phone family or like new phone family)
- Abstract factory pattern: When we have multiple families of object components (E.g. Create one board and one monitor from board family and monitor family).
6. References
http://www.codeproject.com/Articles/716413/Factory-Method-Pattern-vs-Abstract-Factory-Pattern
http://www.codeproject.com/Articles/492900/From-No-Factory-to-Factory-Method
http://rahulrajatsingh.com/2015/02/understanding-and-implementing-factory-pattern-in-c/
Không có nhận xét nào:
Đăng nhận xét