close up photo of programming of codes

codecungnhau.com

Một trang web về kỹ thuật lập trình

Design patterns: Abstract Factory

Mục tiêu

Abstract Factory là một mẫu thiết kế khởi tạo cho phép bạn tạo ra các họ của các đối tượng liên quan mà không cần chỉ định các lớp cụ thể của chúng.

Đặt vấn đề

Hãy tưởng tượng rằng bạn đang tạo một ứng dụng mô phỏng cửa hàng đồ nội thất. Chương trình của bạn bao gồm các lớp sau:

  1. Một nhóm các sản phẩm liên quan, chẳng hạn như: Chair + Sofa + CoffeeTable.
  2. Một số biến thể của nhóm này. Ví dụ, các sản phẩm Chair + Sofa + CoffeeTable có sẵn với các biến thể sau: Modern, Victorian, ArtDeco.
Nhóm sản phẩm và các biến thể của chúng

Bạn cần một cách để tạo các đồ nội thất riêng lẻ sao cho chúng phù hợp với các đồ vật khác trong cùng một nhóm. Khách hàng khá tức giận khi nhận được đồ nội thất không phù hợp.

Ghế sofa kiểu Modern không phù hợp với ghế kiểu Victorian

Ngoài ra, bạn không muốn thay đổi chương trình hiện có khi thêm vào sản phẩm hoặc nhóm sản phẩm mới. Các nhà cung cấp đồ nội thất cập nhật danh mục của họ rất thường xuyên và bạn sẽ không muốn thay đổi lõi chương trình mỗi lần nó xảy ra.

Giải pháp

Điều đầu tiên mà mẫu Abstract Factory gợi ý là khai báo rõ ràng các giao diện cho từng sản phẩm riêng biệt trong nhóm sản phẩm (ví dụ: ghế, sofa hoặc bàn cà phê). Sau đó, bạn có thể để tất cả các biến thể của sản phẩm tuân theo các giao diện đó. Ví dụ: tất cả các biến thể của Chair đều có thể triển khai giao diện Chair; tất cả các biến thể CoffeeTable đều có thể triển khai giao diện CoffeeTable, v.v.

Tất cả các biến thể của cùng một đối tượng phải được chuyển đến một hệ thống phân cấp lớp.

Bước tiếp theo là khai báo Abstract Factory — một giao diện với danh sách các phương thức khởi tạo cho tất cả các sản phẩm thuộc nhóm sản phẩm (createChair, createSofacreateCoffeeTable). Các phương thức này phải trả về các loại sản phẩm trừu tượng được đại diện bởi các giao diện mà chúng tôi đã trích xuất trước đây: Chair, Sofa, CoffeeTable, v.v.

Mỗi phương thức factory tương ứng với một biến thể sản phẩm cụ thể.

Bây giờ, với các biến thể của sản phẩm thì sao? Đối với từng biến thể của một nhóm sản phẩm, chúng ta tạo một lớp factory riêng dựa trên giao diện AbstractFactory. Một factory là một lớp trả lại các sản phẩm của một loại cụ thể. Ví dụ: ModernFurnitureFactory chỉ có thể tạo các đối tượng ModernChair, ModernSofaModernCoffeeTable.

Mã Client phải hoạt động với cả factory và sản phẩm thông qua các giao diện trừu tượng tương ứng của chúng. Điều này cho phép bạn thay đổi loại factory mà bạn truyền cho client, cũng như biến thể sản phẩm mà client nhận được, mà không phá vỡ mã client thực tế.

Client không nên quan tâm đến lớp cụ thể của factory mà nó làm việc cùng.

Giả sử khách hàng (client) muốn một nhà máy (factory) sản xuất một chiếc ghế. Khách hàng không cần phải biết về loại ghế của nhà máy, cũng như không quan trọng loại ghế mà họ nhận được. Cho dù đó là ghế kiểu Modern hay Victorian, khách hàng phải xử lý tất cả các ghế theo cùng một cách, sử dụng thông qua giao diện Chair. Với cách tiếp cận này, điều duy nhất mà khách hàng biết về chiếc ghế là nó thực hiện phương thức sitOn theo một cách nào đó. Ngoài ra, bất kỳ biến thể nào của ghế được trả lại, nó sẽ luôn khớp với loại ghế sofa hoặc bàn cà phê được sản xuất bởi cùng một đối tượng factory.

Còn một điều nữa cần làm rõ, nếu khách hàng chỉ tiếp xúc với các giao diện trừu tượng, thì điều gì tạo ra các đối tượng factory thực sự? Thông thường, ứng dụng tạo một đối tượng factory cụ thể ở giai đoạn khởi tạo. Ngay trước đó, ứng dụng phải chọn loại xuất xưởng tùy thuộc vào cài đặt hoặc môi trường.

Cấu trúc

Cấu trúc một mẫu Abstract Factory
  1. Abstract Product (ProductA, ProductB) khai báo giao diện cho một tập hợp các sản phẩm riêng biệt nhưng có liên quan tạo nên một nhóm sản phẩm.
  2. Concrete Product là các sản phẩm cụ thể triển khai theo các Abstract Product, được nhóm theo các biến thể. Mỗi sản phẩm trừu tượng (chair/sofa) phải được thực hiện trong tất cả các biến thể nhất định (Victorian/Modern).
  3. Giao diện Abstract Factory khai báo một tập hợp các phương thức khởi tạo cho từng sản phẩm trừu tượng.
  4. Concrete Factory thực hiện các phương thức khởi tạo của giao diện Abstract Factory. Mỗi Concrete Factory tương ứng với một biến thể cụ thể của sản phẩm và chỉ tạo ra những biến thể sản phẩm đó.
  5. Mặc dù các Concrete Factory tạo ra các sản phẩm cụ thể, nhưng chữ ký của các phương thức khởi tạo của chúng phải trả về các sản phẩm trừu tượng (abstract product) tương ứng. Bằng cách này, client sử dụng factory sẽ không cần liên kết với biến thể cụ thể của sản phẩm mà nó nhận được từ factory. Client có thể làm việc với bất kỳ biến thể nhà máy / sản phẩm cụ thể nào, miễn là nó giao tiếp với các đối tượng thông qua các giao diện trừu tượng.

Khả năng áp dụng

Sử dụng Abstract Factory khi chương trình của bạn cần hoạt động với nhiều nhóm sản phẩm liên quan khác nhau, nhưng bạn không muốn nó phụ thuộc vào các lớp cụ thể của các sản phẩm đó — chúng có thể chưa được biết trước hoặc bạn chỉ muốn cho phép khả năng mở rộng trong tương lai.

Ưu và nhược điểm

😄😄😄

Bạn có thể chắc chắn rằng các sản phẩm bạn nhận được từ factory tương thích với nhau.

Bạn tránh việc liên kết chặt chẽ giữa sản phẩm cụ thể và client.

Nguyên tắc Đơn trách nhiệm. Bạn có thể trích xuất mã khởi tạo sản phẩm vào một nơi, cập nhật mã dễ dàng hơn.

Nguyên tắc Mở/Đóng. Bạn có thể giới thiệu các biến thể mới của sản phẩm mà không cần phá vỡ mã client hiện có.

🙁🙁🙁

Chương trình có thể trở nên phức tạp hơn mức bình thường, vì rất nhiều giao diện và lớp mới được giới thiệu cùng với mẫu.

Mối quan hệ với các mẫu khác

  • Nhiều thiết kế bắt đầu bằng cách sử dụng Factory Method (ít phức tạp hơn và có thể tùy chỉnh nhiều hơn thông qua các lớp con) và phát triển theo hướng Abstract Factory, Prototype hoặc Builder (linh hoạt hơn nhưng phức tạp hơn).
  • Builder tập trung vào việc xây dựng các đối tượng phức tạp theo từng bước. Abstract Factory chuyên tạo nhóm các đối tượng liên quan. Abstract Factory trả về đối tượng ngay lập tức, trong khi Builder cho phép bạn chạy một số bước xây dựng bổ sung trước khi trả về đối tượng.
  • Các lớp Abstract Factory thường dựa trên một tập hợp các phương thức Factory, nhưng bạn cũng có thể sử dụng Prototype để soạn các phương thức trên các lớp này.
  • Abstract Factory có thể phục vụ như một giải pháp thay thế cho Facade khi bạn chỉ muốn ẩn cách các đối tượng hệ thống con được tạo ra khỏi mã client.
  • Bạn có thể sử dụng Abstract Factory cùng với Bridge. Việc ghép cặp này rất hữu ích khi một số đối tượng trừu tượng được định nghĩa bởi Bridge chỉ có thể hoạt động với các hiện thực cụ thể. Trong trường hợp này, Abstract Factory có thể đóng gói các quan hệ này và ẩn sự phức tạp khỏi mã client.
  • Abstract Factory, Builder và Prototype đều có thể triển khai dưới dạng Singleton.

Mã tham khảo

C++ Design patterns: Abstract Factory

Python Design patterns: Abstract Factory

C# Design patterns: Abstract Factory


Đã đăng vào

trong

bởi

Thẻ:

Bình luận

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *