Translate

Chủ Nhật, 4 tháng 12, 2016

Simple Factory Pattern, Factory Method Pattern, Abstract Factory Pattern

Created by Thang Vu
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. 
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4. class Phone {  
  5. public:  
  6.  virtual void name() = 0;  
  7.  void pack() {  
  8.   cout << "This phone is packed and sent to you soon" << endl;  
  9.  }  
  10. };  
  11.    
  12. class Samsung : public Phone {  
  13. public:  
  14.  void name() {  
  15.    
  16.   cout << "This is a brand new Samsung phone" << endl;  
  17.  }  
  18. };  
  19.    
  20. class Apple : public Phone {  
  21. public:  
  22.  void name() {  
  23.   cout << "This is a brand new Iphone" << endl;  
  24.  }  
  25. };  
  26.    
  27. /* Client code */  
  28. int main() {  
  29.  string userInput = "samsung";  //simulate user input  
  30.  Phone *phone;  
  31.  if (userInput == "samsung") {  
  32.   phone = new Samsung;  
  33.  } else {  
  34.   phone =  new Apple;  
  35.  }  
  36.  phone ->name();  
  37.  phone ->pack();  
  38.  return 0;  
  39. }  
"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.
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4. class Phone {  
  5. public:  
  6.  virtual void name() = 0;  
  7.  void pack() {  
  8.   cout << "This phone is packed and sent to you soon" << endl;  
  9.  }  
  10. };  
  11.    
  12.    
  13. class Samsung : public Phone {  
  14. public:  
  15.  void name() {  
  16.   cout << "This is a brand new Samsung phone" << endl;  
  17.  }  
  18. };  
  19.    
  20.    
  21. class Apple : public Phone {  
  22. public:  
  23.  void name() {  
  24.   cout << "This is a brand new Iphone" << endl;  
  25.  }  
  26. };  
  27.    
  28. class PhoneFactory {  
  29. public:  
  30.  Phone* makePhone(string userInput) {  
  31.   Phone *phone;  
  32.   if (userInput == "samsung") {  
  33.    phone = new Samsung;  
  34.   } else {  
  35.    phone =  new Apple;  
  36.   }  
  37.   phone ->name();  
  38.   phone ->pack();  
  39.   return phone;  
  40.  }  
  41. };  
  42.    
  43. int main() {  
  44.  string userInput = "samsung";        /*simulate user input*/  
  45.  PhoneFactory *phoneFactory = new PhoneFactory;  
  46.  Phone *phone = phoneFactory->makePhone(userInput);  
  47.  return 0;  
  48.    
  49. }  
?
The shop operation has increased a lot and the boss wants to sell like new smart phones. Then, the code in PhoneFactory class can be modified as follows.

  1. class PhoneFactory {  
  2. public:  
  3.  Phone* makePhone(string userInput) {  
  4.   Phone *phone;  
  5.   if (userInput == "samsung") {  
  6.    phone = new Samsung;  
  7.   } else if(userInput == "apple") {  
  8.    phone =  new Apple;  
  9.   } else if(userInput == "like new samsung){  
  10.    phone = new LikeNewSamsung;  
  11.   } else {  
  12.    phone = new LikeNewApple;  
  13.   }  
  14.   phone ->name();  
  15.   phone ->pack();  
  16.   return phone;  
  17.  }  
  18. }; 
It actually works, but breaks Single Responsibility Principle saying a class should have only one reason to change. Here,. the PhoneFactory class will be changed

  • When others brand new phones are added
  • When others like new phones are added
To solve this problem, we may think about are two separate Factory classes, one for brand new phones, the other for like new phones.


  1. class BrandNewPhoneFactory {  
  2. public:  
  3.  Phone* makePhone(string userInput) {  
  4.   Phone *phone;  
  5.   if (userInput == "samsung") {  
  6.    phone = new Samsung;  
  7.   } else {  
  8.    phone =  new Apple;  
  9.   }  
  10.   phone ->name();  
  11.   phone ->pack();  
  12.   return phone;  
  13.  }  
  14. };  
  15. class LikeNewPhoneFactory{  
  16.   Phone *phone;  
  17.   if (userInput == "samsung") {  
  18.    phone = new LikeNewSamsung;  
  19.   } else {  
  20.    phone =  new LikeNewApple;  
  21.   }  
  22.   phone ->name();  
  23.   phone ->pack();  
  24.   return phone;  
  25.  }  
  26. };  



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

For now, if a new phone group is add. a new factory will be derived from BasePhoneFactory, encapsulating creation of all concrete classes.

  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.    
  5. class Phone {  
  6. public:  
  7.  virtual void name() = 0;  
  8.  void pack() {  
  9.   cout << "This phone is packed and sent to you soon" << endl;  
  10.  }  
  11. };  
  12.    
  13.    
  14. class Samsung : public Phone {  
  15. public:  
  16.  void name() {  
  17.   cout << "This is a brand new Samsung phone" << endl;  
  18.  }  
  19. };  
  20.    
  21. class Apple : public Phone {  
  22. public:  
  23.  void name() {  
  24.   cout << "This is a brand new Iphone" << endl;  
  25.  }  
  26. };  
  27.    
  28. class LikeNewSamsung: public Phone {  
  29. public:  
  30.  void name() {  
  31.   cout << "This is a like new Samsung phone"<< endl;  
  32.  }  
  33. };  
  34.    
  35. class LikeNewApple: public Phone {  
  36. public:  
  37.  void name() {  
  38.   cout << "This is a like new Apple phone"<< endl;  
  39.  }  
  40. };  
  41.    
  42.    
  43. class PhoneFactory {  
  44. public:  
  45.  Phone *makePhone(string userInput) {  
  46.   Phone *phone = this -> getPhone(userInput);  
  47.   phone->name();  
  48.   phone->pack();  
  49.   return phone;  
  50.  }  
  51.  virtual Phone *getPhone(string userInput) = 0;  
  52. };  
  53.    
  54. class BrandNewPhoneFactory : public PhoneFactory{  
  55. public:  
  56.  Phone *getPhone(string userInput) {  
  57.   if (userInput == "samsung") {  
  58.    return new Samsung;  
  59.   } else {  
  60.    return  new Apple;  
  61.   }  
  62.  }  
  63. };  
  64.    
  65. class LikeNewPhoneFactory : public PhoneFactory{  
  66. public:  
  67.  Phone *getPhone(string userInput) {  
  68.   if (userInput == "samsung") {  
  69.    return new LikeNewSamsung;  
  70.   } else {  
  71.    return new LikeNewApple;  
  72.   }  
  73.  }  
  74. };  
  75.    
  76.    
  77. int main() {  
  78.  string userInput = "samsung";        /*simulate user input*/  
  79.  PhoneFactory *phoneFactory = new LikeNewPhoneFactory;  
  80.  Phone *phone = phoneFactory->makePhone(userInput);  
  81.  return 0;  
  82. }  

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
We define a AbstractFactory as a contract of derived class with two abstract methods: makeBoard and makeMonitor. Three derived class based on phone quality create the corresponding components.

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.
?

  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.    
  5.    
  6. class Board {  
  7. public:  
  8.  virtual void showBoard() = 0;  
  9. };  
  10.    
  11. class ExpensiveBoard : public Board {  
  12. public:  
  13.  void showBoard() {  
  14.   cout << "this is an expensive board" << endl;  
  15.  }  
  16. };  
  17.    
  18. class CheapBoard : public Board {  
  19. public:  
  20.  void showBoard() {  
  21.   cout << "This is a cheap board" << endl;  
  22.  }  
  23. };  
  24.    
  25. class Monitor {  
  26. public:  
  27.  virtual void showMonitor() = 0;  
  28. };  
  29.    
  30. class ExpensiveMonitor : public Monitor {  
  31. public:  
  32.  void showMonitor() {  
  33.   cout << "This is an expensive monitor" << endl;  
  34.  }  
  35. };  
  36.    
  37. class CheapMonitor : public Monitor {  
  38. public:  
  39.  void showMonitor() {  
  40.   cout << "This is a cheap monitor" << endl;  
  41.  }  
  42. };  
  43.    
  44. class AbstractFactory {  
  45. public:  
  46.  virtual Board *makeBoard() = 0;  
  47.  virtual Monitor *makeMonitor() = 0;  
  48. };  
  49.    
  50. class ExpensivePhoneFactory : public AbstractFactory {  
  51. public:  
  52.  Board *makeBoard() {  
  53.   return new ExpensiveBoard;  
  54.  }  
  55.  Monitor *makeMonitor() {  
  56.   return new ExpensiveMonitor;  
  57.  }  
  58. };  
  59.    
  60. class MediumPhoneFactory : public AbstractFactory {  
  61. public:  
  62.  Board *makeBoard() {  
  63.   return new CheapBoard;  
  64.  }  
  65.  Monitor *makeMonitor() {  
  66.   return new ExpensiveMonitor;  
  67.  }  
  68. };  
  69.    
  70. class CheapPhoneFactory : public AbstractFactory {  
  71. public:  
  72.  Board *makeBoard() {  
  73.   return new ExpensiveBoard;  
  74.  }  
  75.  Monitor *makeMonitor() {  
  76.   return new ExpensiveMonitor;  
  77.  }  
  78. };  
  79.    
  80. class PhoneAssembler {  
  81. private:  
  82.  AbstractFactory *factoryType;  
  83. public:  
  84.  typedef enum {  
  85.   EXPENSIVE_PHONE,  
  86.   MEDIUM_PHONE,  
  87.   CHEAP_PHONE  
  88.  } PHONE_TYPE;  
  89.  PhoneAssembler(PHONE_TYPE type) {  
  90.   if (type == EXPENSIVE_PHONE) {  
  91.    factoryType = new ExpensivePhoneFactory;  
  92.   } else if (type == MEDIUM_PHONE) {  
  93.    factoryType = new MediumPhoneFactory;  
  94.   } else {  
  95.    factoryType = new CheapPhoneFactory;  
  96.   }  
  97.  }  
  98.  void assemble() {  
  99.   Board *board = factoryType->makeBoard();  
  100.   Monitor *monitor = factoryType->makeMonitor();  
  101.   board->showBoard();  
  102.   monitor->showMonitor();  
  103.   cout << "The phone is assembled" << endl;  
  104.  }  
  105. };  
  106.    
  107. int main() {  
  108.  PhoneAssembler::PHONE_TYPE userInput = PhoneAssembler::EXPENSIVE_PHONE;        /*simulate user input*/  
  109.  PhoneAssembler *phoneAssembler = new PhoneAssembler(userInput);  
  110.  phoneAssembler->assemble();  
  111.  return 0;  
  112. }  

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). 
Source code of the above examples can download in here.

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