Tìm hiểu về Abstract Factory Pattern và các ứng dụng? – FitWP

Trong nội dung trước, tất cả chúng ta đã cùng với nhau tìm hiểu về loại thiết kế pattern trước tiên đưa tên Factory Method Pattern. Trong nội dung này, tất cả chúng ta hãy cùng với nhau tìm hiểu về một người anh em khác của Factory Method Pattern đưa tên ‘Abstract Factory Pattern’ nhé!

Abstract Factory Pattern là gì thế nhỉ?

Cũng giống như Factory Method Pattern, Abstract Factory Pattern cũng là một thiết kế pattern thuộc nhóm khởi tạo (creational patterns) không những thế nó thời thượng hơn Factory Method Pattern ở chỗ nó cho phép tạo thành một super factory dùng để tạo thành các factory khác.

Để dễ hình dung hơn, hãy tưởng tượng Abstract Factory như là một nhà xưởng lớn chứa nhiều nhà xưởng nhỏ, trong các nhà xưởng đó có những xưởng sản xuất, các xưởng đó tạo thành những sản phẩm khác nhau.

Vấn đề đề ra

Giả sử bạn đang tiến triển một ứng dụng phục vụ cho cửa tiệm kinh doanh vật dụng nội thất của mình đưa tên FurnitureShopApp. Code hiện giờ của các bạn đang bao gồm một số class đại diện cho:

  • Nhóm các sản phẩm đi kèm với nhau, chẳng hạn: Chair + Sofa + CoffeeTable
  • Loại biến thể khác nhau của mỗi nhóm sản phẩm. Chẳng hạn, cùng nhóm sản phẩm Chair + Sofa + CoffeeTable nhưng lại có nhiều loại biến thể khác nhau, như: Modern, Victorian, ArtDeco.

Một số vấn đề đặt ra

Ngoài ra trong thời gian gần đây bạn liên tục thu được những comment không mấy tích cực từ phía KH. Lý do là do những sản phẩm mà họ thu được sau thời điểm đặt hàng đều chẳng hề đồng bộ và tương thích với nhau.

Ghế sofa kiểu hiện đại không hề ăn khớp và tương thích với ghế bành kiểu Victoria

Là người trực tiếp tiến triển ứng dụng, bạn chẳng thể bình chân như vại trước tình hình không mấy tích cực hiện giờ. Bạn mong muốn tìm thấy cách nào đó để có thể tạo thành các món đồ nội thất riêng rẽ mà vẫn bảo đảm rằng chúng đồng bộ với các món đồ nội thất sót lại trong cùng nhóm sản phẩm tương ứng. không chỉ thế, vì các partners phân phối đồ nội thất của cửa tiệm đều update mục lục các sản phẩm của họ rất thường xuyên nên bạn cũng mong muốn tìm thấy cách nào đó để có thể giới hạn ít nhất việc phải thường xuyên quay lại biến đổi các đoạn code chủ chốt mỗi khi những lần update mục lục này xảy ra.

Biện pháp

Rất may, Abstract Factory Pattern chính là chiếc phao có thể giải nguy cho bạn trong trường hợp này. Điều trước tiên mà Abstract Factory Pattern đề nghị chính là việc khai báo từng interface cho từng sản phẩm biệt lập trong một nhóm sản phẩm (chẳng hạn: Chair, Sofa, CoffeeTable,…). Sau đó, mỗi loại biến thể của từng sản phẩm biệt lập sẽ phải tiến hành implements interface đó. Chẳng hạn, toàn bộ các loại biến thể của Chair phải tiến hành implements interface Chair, toàn bộ các loại biến thể của Sofa phải tiến hành implements interface Sofa,…

Xem Thêm  Làm cách nào để làm cho phông chữ mỏng hơn trong css? - làm thế nào để làm cho văn bản mỏng hơn trong css

Abstract Factory Pattern chính là giải pháp cho bạn

Kế tiếp các bạn cần khai báo một Abstract Factory – một interface bao gồm các factory method cho từng sản phẩm biệt lập trong một nhóm sản phẩm (chẳng hạn: createdChair, createdSofa và createdCoffeeTable,…). Mỗi factory method này sẽ trả về kiểu dữ liệu ứng với loại interface đã được khai báo của nó (chẳng hạn: Chair, Sofa, CoffeeTable,…)

Vậy còn nhóm các sản phẩm ứng với mỗi loại biến thể thì sao? Tất cả chúng ta sẽ tạo thành các factory biệt lập dựa vào interface AbstractFactory đã được khai báo, mỗi factory này sẽ lần lượt implement các factory method của AbstractFactory interface và trả về toàn bộ các sản phẩm của một nhóm rõ ràng, chẳng hạn: ModernFactory chỉ có thể tạo các đối tượng ModernChair, ModernSofa và ModernCoffeeTable.

Với setup như trên, hiện thời khi cần nhóm sản phẩm thuộc loại biến thể nào đó, phía KH chỉ cần nêu ra factory tương ứng là đã có thể thu được đúng các sản phẩm bảo đảm đồng bộ và tương thích với nhau. Thật tuyệt đúng không nào.

Kết cấu của Abstract Factory Pattern

Abstract Factory Pattern bao gồm năm thành phần căn bản là: Abstract Factory, Concrete Factory, Abstract Product, Concrete ProductClient.

Cấu trúc của Abstract Factory Pattern

Trong số đó:

  • AbstractFactory: Khai báo dạng interface hoặc abstract class chứa các phương pháp để tạo thành các đối tượng abstract.
  • ConcreteFactory: Xây dựng, setup các phương pháp tạo các đối tượng rõ ràng.
  • AbstractProduct: Khai báo dạng interface hoặc abstract class để khái niệm đối tượng abstract.
  • ConcreteProduct: Seting của các đối tượng rõ ràng, setup các phương pháp được quy định tại AbstractProduct.
  • Client: là đối tượng sử dụng AbstractFactory và các AbstractProduct.

Khắc phục vấn đề đã đề ra

Nói lý thuyết vậy là đủ rồi, cùng thử vận dụng Abstract Factory Pattern để khắc phục vấn đề đã đề ra nhé.

Trước hết tất cả chúng ta sẽ tạo thành 3 interface tương ứng với 3 sản phẩm khác nhau là Chair, Sofa CoffeeTable.

<?php
       interface Chair {
             public function hasLegs(): string;
             public function sitOn(): string;
       }

       interface Sofa {
             public function hasLegs(): string;
             public function sitOn(): string;
       }

       interface CoffeeTable {
             public function hasLegs(): string;
             public function putOn(): string;
       }
?>

Sau thời điểm tạo xong các interface trên, tất cả chúng ta tiến hành tạo thành các class ứng với mỗi loại biến thể của từng sản phẩm, mỗi class này sẽ phải implements interface tương ứng của nó.

<?php
       class VictorianChair implements Chair {
             public function hasLegs(): string {
                   return 'Victorian Chair has four long legs.'
             }

             public function sitOn(): string {
                   return 'Sit on Victorian Chair.';
             }
       }

       class ArtDecoChair implements Chair {
             public function hasLegs(): string {
                   return 'Art Deco Chair has four short legs.';
             }

             public function sitOn(): string {
                   return 'Sit on Art Deco Chair.';
             }
       }

       class ModernChair implements Chair {
             public function hasLegs(): string {
                   return 'Modern Chair has no legs.';
             }

             public function sitOn(): string {
                   return 'Sit on Modern Chair.';
             }
       }

       class VictorianSofa implements Sofa {
             public function hasLegs(): string {
                   return 'Victorian Sofa has four long legs.';
             }
 
             public function sitOn(): string {
                   return 'Sit on Victorian Sofa.';
             }
       }

       class ArtDecoSofa implements Sofa {
             public function hasLegs(): string {
                   return 'Art Deco Sofa has four short legs.';
             }

             public function sitOn(): string {
                   return 'Sit on Art Deco Sofa.';
             }
       }

       class ModernSofa implements Sofa {
             public function hasLegs(): string {
                   return 'Modern Sofa has no legs.';
             }

             public function sitOn(): string {
                   return 'Sit on Modern Sofa.';
             }
       }

       class VictorianCoffeeTable implements CoffeeTable {
             public function hasLegs(): string {
                   return 'Victorian Coffee Table has four long legs.';
             }

             public function putOn(): string {
                   return 'Put something on Victorian Coffee Table.';
             }
       }

       class ArtDecoCoffeeTable implements CoffeeTable {
             public function hasLegs(): string {
                   return 'Art Deco Coffee Table has only one leg.';
             }

             public function putOn(): string {
                   return 'Put something on Art Deco Table.';
             }
       }

       class ModernCoffeeTable implements CoffeeTable {
             public function hasLegs(): string {
                   return 'Modern Coffee Table has four short legs.';
             }
 
             public function putOn(): string {
                   return 'Put something on Modern Coffee Table.';
             }
       }
?>

Kế tiếp, tất cả chúng ta sẽ tiến hành tạo thành FurnitureFactory – một interface bao gồm các factory method cho từng sản phẩm biệt lập trong một nhóm sản phẩm. Mỗi factory method này sẽ trả về kiểu dữ liệu ứng với loại interface đã được khai báo của nó.

<?php
       interface FurnitureFactory {
             public function createChair(): Chair;
             public function createSofa(): Sofa;
             public function createCoffeeTable(): CoffeeTable;
       }
?>

Tiếp đó, tất cả chúng ta tiến hành tạo thành nhóm các sản phẩm ứng với mỗi loại biến thể bằng cách tạo thành các factory biệt lập dựa vào interface AbstractFactory đã được khai báo, mỗi factory này sẽ lần lượt override các factory method của AbstractFactory interface và trả về toàn bộ các sản phẩm của một nhóm rõ ràng.

<?php
       class VictorianFurnitureFactory {
             public function createChair(): Chair {
                   return new VictorianChair();
             }

             public function createSofa(): Sofa {
                   return new VictorianSofa();
             }

             public function createCoffeeTable(): CoffeeTable {
                   return new VictorianCoffeeTable();
             }
       }

       class ArtDecoFactory {
             public function createChair(): Chair {
                   return new ArtDecoChair();
             }

             public function createSofa(): Sofa {
                   return new ArtDecoSofa();
             }

             public function createCoffeeTable(): CoffeeTable {
                   return new ArtDecoCoffeeTable();
             }
       }

       class ModernFactory {
             public function createChair(): Chair {
                   return new ModernChair();
             }

             public function createSofa(): Sofa {
                   return new ModernSofa();
             }

             public function createCoffeeTable(): CoffeeTable {
                   return new ModernCoffeeTable();
             }
       }
?>

Vậy là phần setup đã xong, cùng chạy thử xem kết quả như vậy nào nhé!

function clientCode(FurnitureFactory $factory)
{
      $chair = $factory->createChair();
      $sofa = $factory->createSofa();
      $coffeeTable = $factory->createCoffeeTable();

      echo $chair->hasLegs() . "n";
      echo $chair->sitOn() . "n";
      echo $sofa->hasLegs() . "n";
      echo $sofa->sitOn() . "n";
      echo $coffeeTable->hasLegs() . "n";
      echo $coffeeTable->sitOn() . "n";
}

      echo "* Victorian Furniture Factoryn";
      clientCode(new VictorianFurnitureFactory());

      echo "n";

      echo "* Art Deco Factoryn";
      clientCode(new ArtDecoFactory());

      echo "n";

      echo "* Modern Factoryn";
      clientCode(new ModernFactory());

Kết quả nhận được:

* Victorian Furniture Factory
Victorian Chair has four long legs.
Sit on Victorian Chair.
Victorian Sofa has four long legs.
Sit on Victorian Sofa.
Victorian Coffee Table has four long legs.
Put something on Victorian Coffee Table.

* Art Deco Factory
Art Deco Chair has four short legs.
Sit on Art Deco Chair.
Art Deco Sofa has four short legs.
Sit on Art Deco Sofa.
Art Deco Coffee Table has only one leg.
Put something on Art Deco Table.

* Modern Factory
Modern Chair has no legs.
Sit on Modern Chair.
Modern Sofa has no legs.
Sit on Modern Sofa.
Modern Coffee Table has four short legs.
Put something on Modern Coffee Table.

Như thế, tất cả chúng ta có thể thấy rằng hiện thời khi cần nhóm sản phẩm thuộc loại biến thể nào đó, phía KH chỉ cần nêu ra factory tương ứng là đã có thể thu được đúng các sản phẩm bảo đảm đồng bộ và tương thích với nhau.

Xem Thêm  Mảng JavaScript - mảng javascript của các đối tượng

Ưu và khuyết điểm của Abstract Factory Pattern

Ưu thế

  • Abstract Factory Pattern giúp bảo đảm rằng các product mà bạn thu được từ một factory đều tương thích với nhau
  • Abstract Factory Pattern giúp giới hạn sự lệ thuộc giữa creator và concrete products.
  • Abstract Factory Pattern giúp gom các đoạn code tạo thành product vào một nơi trong chương trình, nhờ đó giúp dễ theo dõi và thao tác.
  • Với Abstract Factory Pattern, tất cả chúng ta có thể dễ chịu thêm nhiều loại product mới vào chương trình mà không làm biến đổi các đoạn code nền móng đã có trước đây.

Khuyết điểm

  • Code có thể trở nên nhiều hơn và cầu kỳ hơn do đòi hỏi phải sử dụng nhiều class mới có thể setup được pattern này.

Tổng kết

Trong nội dung này, tất cả chúng ta đã cùng với nhau tìm hiểu về Abstract Factory Pattern, ước ao pattern này sẽ giúp ích cho các bạn trong tương lai. Trong nội dung kế tiếp của series, mình sẽ tiếp tục giới thiệu cho các bạn một thiết kế pattern mới toanh thuộc nhóm khởi tạo đưa tên ‘Builder’. Các bạn nhớ đón xem nhé.

Viết một bình luận