development:software-architecture:design-patterns:factory
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
development:software-architecture:design-patterns:factory [2024/08/08 04:47] – tungnt | development:software-architecture:design-patterns:factory [2024/08/19 09:29] (current) – tungnt | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Factory ====== | ====== Factory ====== | ||
- | Hãy tưởng tượng rằng bạn đang tạo một ứng dụng quản lý hậu cần. Phiên bản đầu tiên của ứng dụng | + | Trong số những kiểu mẫu thiết kế hay design pattern trong PHP thì Factory là một trong những pattern |
- | Sau một thời gian, ứng dụng của bạn trở nên khá phổ biến. Mỗi ngày, bạn nhận được hàng chục yêu cầu từ các công ty vận tải biển để tích hợp hậu cần đường biển vào ứng dụng. | + | ===== Simple Factory Pattern ===== |
- | Tin tuyệt vời, phải | + | **Cách code khi không |
- | Kết quả là, bạn sẽ có một mã khá khó chịu, chứa đầy các điều kiện chuyển đổi hành vi của ứng dụng tùy thuộc vào lớp đối tượng phương tiện giao thông. | + | <file php> |
+ | //... | ||
- | Trong số những kiểu mẫu thiết kế hay design pattern trong PHP thì Factory là một trong những pattern | + | private function getServiceLogistics($cargoVolume) |
+ | { | ||
+ | switch ($cargoVolume) { | ||
+ | case 10: | ||
+ | return [ | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ]; | ||
+ | case 20: | ||
+ | return [ | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ]; | ||
+ | default: | ||
+ | return []; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //... | ||
+ | |||
+ | var_dump($this-> | ||
+ | </ | ||
+ | |||
+ | **Cách code khi dùng Simple Factory Pattern: | ||
+ | |||
+ | <file php> | ||
+ | /** | ||
+ | * ServiceLogistics | ||
+ | */ | ||
+ | class ServiceLogistics{ | ||
+ | private $name; | ||
+ | private $door; | ||
+ | private $price; | ||
+ | |||
+ | /** | ||
+ | * __construct | ||
+ | * | ||
+ | * @param | ||
+ | * @param | ||
+ | * @param | ||
+ | * @return void | ||
+ | */ | ||
+ | public function __construct($name = "Truck 10", $door = 6, $price = 250000) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * getTransport | ||
+ | * | ||
+ | * @param | ||
+ | * @return void | ||
+ | * | ||
+ | * Đã chuyển logic thành các class thực | ||
+ | * Mong muốn khi thêm một loại mới mà không cần cập nhật lại function getTransport -> sử dụng | ||
+ | */ | ||
+ | public static function getTransport($cargoVolume) | ||
+ | { | ||
+ | switch ($cargoVolume) { | ||
+ | case 10: | ||
+ | return new ServiceLogistics(); | ||
+ | case 20: | ||
+ | return new ServiceLogistics(' | ||
+ | default: | ||
+ | return []; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | var_dump(ServiceLogistics:: | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Ví dụ khác:** | ||
<file php> | <file php> | ||
Line 35: | Line 112: | ||
} | } | ||
</ | </ | ||
- | |||
Ở trên chúng ta có hai class là XMLParser và JSONParser dùng để parse nội dung của tập tin tuỳ thuộc vào tập tin được lưu dưới định dạng là XML hay JSON. Tuy nhiên thay vì tạo parser object trực tiếp từ hai class này mà thông qua một Factory class ParserFactory. | Ở trên chúng ta có hai class là XMLParser và JSONParser dùng để parse nội dung của tập tin tuỳ thuộc vào tập tin được lưu dưới định dạng là XML hay JSON. Tuy nhiên thay vì tạo parser object trực tiếp từ hai class này mà thông qua một Factory class ParserFactory. | ||
Như ví dụ trên nếu chúng ta thay đổi tên của class XMLParser thì chúng ta chỉ cần thay đổi một dòng code trong method construct của ParserFactory. Nếu như chúng ta không sử dụng Factory thì sẽ phải thay đổi ở tất cả các object được tạo từ class XMLParser. | Như ví dụ trên nếu chúng ta thay đổi tên của class XMLParser thì chúng ta chỉ cần thay đổi một dòng code trong method construct của ParserFactory. Nếu như chúng ta không sử dụng Factory thì sẽ phải thay đổi ở tất cả các object được tạo từ class XMLParser. | ||
+ | |||
+ | ===== Factory Method Pattern ===== | ||
+ | |||
+ | Hãy tưởng tượng rằng bạn đang tạo một ứng dụng quản lý hậu cần. Phiên bản đầu tiên của ứng dụng của bạn chỉ có thể xử lý vận chuyển bằng xe tải, vì vậy phần lớn mã của bạn nằm trong lớp Truck. | ||
+ | |||
+ | Sau một thời gian, ứng dụng của bạn trở nên khá phổ biến. Mỗi ngày, bạn nhận được hàng chục yêu cầu từ các công ty vận tải biển để tích hợp hậu cần đường biển vào ứng dụng. | ||
+ | |||
+ | Tin tuyệt vời, phải không? Nhưng còn mã thì sao? Hiện tại, hầu hết mã của bạn được ghép nối với lớp Truck. Việc thêm Ships vào ứng dụng sẽ yêu cầu phải thay đổi toàn bộ cơ sở mã. Hơn nữa, nếu sau này bạn quyết định thêm một loại phương tiện giao thông khác vào ứng dụng, có thể bạn sẽ cần phải thực hiện lại tất cả những thay đổi này. | ||
+ | |||
+ | Kết quả là, bạn sẽ có một mã khá khó chịu, chứa đầy các điều kiện chuyển đổi hành vi của ứng dụng tùy thuộc vào lớp đối tượng phương tiện giao thông. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | **Tóm lại:** Method Factory dùng khi có nhiều cách thức khác nhau để đạt được 1 mục tiêu, và để khi thêm một cách thức mới thì chúng ta chỉ cần thêm một Class mới tương ứng không phải sửa quá nhiều code trong function xử lý. | ||
+ | |||
+ | **Ví dụ 1:** | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | namespace RefactoringGuru\FactoryMethod\Conceptual; | ||
+ | |||
+ | /** | ||
+ | * The Creator class declares the factory method that is supposed to return an | ||
+ | * object of a Product class. The Creator' | ||
+ | * implementation of this method. | ||
+ | */ | ||
+ | abstract class Creator | ||
+ | { | ||
+ | /** | ||
+ | * Note that the Creator may also provide some default implementation of the | ||
+ | * factory method. | ||
+ | */ | ||
+ | abstract public function factoryMethod(): | ||
+ | |||
+ | /** | ||
+ | * Also note that, despite its name, the Creator' | ||
+ | * not creating products. Usually, it contains some core business logic that | ||
+ | * relies on Product objects, returned by the factory method. Subclasses can | ||
+ | * indirectly change that business logic by overriding the factory method | ||
+ | * and returning a different type of product from it. | ||
+ | */ | ||
+ | public function someOperation(): | ||
+ | { | ||
+ | // Call the factory method to create a Product object. | ||
+ | $product = $this-> | ||
+ | // Now, use the product. | ||
+ | $result = " | ||
+ | $product-> | ||
+ | |||
+ | return $result; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Concrete Creators override the factory method in order to change the | ||
+ | * resulting product' | ||
+ | */ | ||
+ | class ConcreteCreator1 extends Creator | ||
+ | { | ||
+ | /** | ||
+ | * Note that the signature of the method still uses the abstract product | ||
+ | * type, even though the concrete product is actually returned from the | ||
+ | * method. This way the Creator can stay independent of concrete product | ||
+ | * classes. | ||
+ | */ | ||
+ | public function factoryMethod(): | ||
+ | { | ||
+ | return new ConcreteProduct1(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class ConcreteCreator2 extends Creator | ||
+ | { | ||
+ | public function factoryMethod(): | ||
+ | { | ||
+ | return new ConcreteProduct2(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The Product interface declares the operations that all concrete products must | ||
+ | * implement. | ||
+ | */ | ||
+ | interface Product | ||
+ | { | ||
+ | public function operation(): | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Concrete Products provide various implementations of the Product interface. | ||
+ | */ | ||
+ | class ConcreteProduct1 implements Product | ||
+ | { | ||
+ | public function operation(): | ||
+ | { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class ConcreteProduct2 implements Product | ||
+ | { | ||
+ | public function operation(): | ||
+ | { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The client code works with an instance of a concrete creator, albeit through | ||
+ | * its base interface. As long as the client keeps working with the creator via | ||
+ | * the base interface, you can pass it any creator' | ||
+ | */ | ||
+ | function clientCode(Creator $creator) | ||
+ | { | ||
+ | // ... | ||
+ | echo " | ||
+ | . $creator-> | ||
+ | // ... | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The Application picks a creator' | ||
+ | * environment. | ||
+ | */ | ||
+ | echo "App: Launched with the ConcreteCreator1.\n"; | ||
+ | clientCode(new ConcreteCreator1()); | ||
+ | echo " | ||
+ | |||
+ | echo "App: Launched with the ConcreteCreator2.\n"; | ||
+ | clientCode(new ConcreteCreator2()); | ||
+ | </ | ||
+ | |||
+ | **Ví dụ 2:** | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | namespace RefactoringGuru\FactoryMethod\RealWorld; | ||
+ | |||
+ | /** | ||
+ | * The Creator declares a factory method that can be used as a substitution for | ||
+ | * the direct constructor calls of products, for instance: | ||
+ | * | ||
+ | * - Before: $p = new FacebookConnector(); | ||
+ | * - After: $p = $this-> | ||
+ | * | ||
+ | * This allows changing the type of the product being created by | ||
+ | * SocialNetworkPoster' | ||
+ | */ | ||
+ | abstract class SocialNetworkPoster | ||
+ | { | ||
+ | /** | ||
+ | * The actual factory method. Note that it returns the abstract connector. | ||
+ | * This lets subclasses return any concrete connectors without breaking the | ||
+ | * superclass' | ||
+ | */ | ||
+ | abstract public function getSocialNetwork(): | ||
+ | |||
+ | /** | ||
+ | * When the factory method is used inside the Creator' | ||
+ | * subclasses may alter the logic indirectly by returning different types of | ||
+ | * the connector from the factory method. | ||
+ | */ | ||
+ | public function post($content): | ||
+ | { | ||
+ | // Call the factory method to create a Product object... | ||
+ | $network = $this-> | ||
+ | // ...then use it as you will. | ||
+ | $network-> | ||
+ | $network-> | ||
+ | $network-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Creator supports Facebook. Remember that this class also | ||
+ | * inherits the ' | ||
+ | * classes that the Client actually uses. | ||
+ | */ | ||
+ | class FacebookPoster extends SocialNetworkPoster | ||
+ | { | ||
+ | private $login, $password; | ||
+ | |||
+ | public function __construct(string $login, string $password) | ||
+ | { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function getSocialNetwork(): | ||
+ | { | ||
+ | return new FacebookConnector($this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Creator supports LinkedIn. | ||
+ | */ | ||
+ | class LinkedInPoster extends SocialNetworkPoster | ||
+ | { | ||
+ | private $email, $password; | ||
+ | |||
+ | public function __construct(string $email, string $password) | ||
+ | { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function getSocialNetwork(): | ||
+ | { | ||
+ | return new LinkedInConnector($this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The Product interface declares behaviors of various types of products. | ||
+ | */ | ||
+ | interface SocialNetworkConnector | ||
+ | { | ||
+ | public function logIn(): void; | ||
+ | |||
+ | public function logOut(): void; | ||
+ | |||
+ | public function createPost($content): | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Product implements the Facebook API. | ||
+ | */ | ||
+ | class FacebookConnector implements SocialNetworkConnector | ||
+ | { | ||
+ | private $login, $password; | ||
+ | |||
+ | public function __construct(string $login, string $password) | ||
+ | { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function logIn(): void | ||
+ | { | ||
+ | echo "Send HTTP API request to log in user $this-> | ||
+ | " | ||
+ | } | ||
+ | |||
+ | public function logOut(): void | ||
+ | { | ||
+ | echo "Send HTTP API request to log out user $this-> | ||
+ | } | ||
+ | |||
+ | public function createPost($content): | ||
+ | { | ||
+ | echo "Send HTTP API requests to create a post in Facebook timeline.\n"; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Product implements the LinkedIn API. | ||
+ | */ | ||
+ | class LinkedInConnector implements SocialNetworkConnector | ||
+ | { | ||
+ | private $email, $password; | ||
+ | |||
+ | public function __construct(string $email, string $password) | ||
+ | { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function logIn(): void | ||
+ | { | ||
+ | echo "Send HTTP API request to log in user $this-> | ||
+ | " | ||
+ | } | ||
+ | |||
+ | public function logOut(): void | ||
+ | { | ||
+ | echo "Send HTTP API request to log out user $this-> | ||
+ | } | ||
+ | |||
+ | public function createPost($content): | ||
+ | { | ||
+ | echo "Send HTTP API requests to create a post in LinkedIn timeline.\n"; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The client code can work with any subclass of SocialNetworkPoster since it | ||
+ | * doesn' | ||
+ | */ | ||
+ | function clientCode(SocialNetworkPoster $creator) | ||
+ | { | ||
+ | // ... | ||
+ | $creator-> | ||
+ | $creator-> | ||
+ | // ... | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * During the initialization phase, the app can decide which social network it | ||
+ | * wants to work with, create an object of the proper subclass, and pass it to | ||
+ | * the client code. | ||
+ | */ | ||
+ | echo " | ||
+ | clientCode(new FacebookPoster(" | ||
+ | echo " | ||
+ | |||
+ | echo " | ||
+ | clientCode(new LinkedInPoster(" | ||
+ | </ | ||
+ | |||
+ | ===== Abstract Factory Pattern ===== | ||
+ | |||
+ | [[development: | ||
====== Tham khảo ====== | ====== Tham khảo ====== | ||
- | * https:// | + | * https:// |
+ | * https://refactoring.guru/ | ||
+ | * https:// |
development/software-architecture/design-patterns/factory.1723092458.txt.gz · Last modified: 2024/08/08 04:47 by tungnt