[Design Patterns #3] Factory Pattern – Phần 2: Abstract Factory

Ở phần 1 của Factory Pattern mình đã giới thiệu cho các bạn về Factory Method. Trong bài viết này mình sẽ tiếp tục giới thiệu tới mọi người pattern thứ 2 trong Factory Pattern là Abstract Factory. Nếu ai chưa đọc phần 1 bên trên nên đọc trước khi đọc bài này để có thể tiếp thu các kiến thức trong bài này hiệu quả nhất.

Bài toán cần giải quyết

Giả sử chúng ta đang xây dựng một ứng dụng có UI (User Interface – giao diện người dùng), và ứng dụng hỗ trợ hiển thị theo nhiều style (trong bài này gọi là themes). Các theme khác nhau sẽ có hình dáng và màu sắc của các đối tượng đồ họa (trong bài này gọi là widgets) như: scroll bar, button, window, … khác nhau.

Nếu trong code chúng ta tạo các instance của widget một cách trực tiếp thì code của chúng ta sẽ bị trùng lặp rất nhiều và sẽ phải sử dụng rất nhiều các câu lệnh điều kiện để xử lý việc chuyển đổi theme. Việc này dẫn đến code khó bảo trì, khó reuse, nói chung là code sẽ bốc mùi.

Trong trường hợp này áp dụng Abstract Factory sẽ rất phù hợp.

Áp dụng Abstract Factory

Để có thể dễ dàng xử lý việc chuyển đổi giữa các theme thì trong code của ứng dụng chúng ta không nên khởi tạo các instance widgets một cách trực tiếp cho các theme cụ thể. Thay vào đó chúng ta sẽ định nghĩa một class là WidgetFactory đóng vai trò Interface, trong đó khai báo các methods để tạo instance cho mỗi loại widget. Việc implement cụ thể các methods đó sẽ được thực hiện ở các subclass của WidgetFactory ứng với mỗi theme.

Ngoài ra cũng cần phải định nghĩa Interface chung cho mỗi loại widget, implementation cụ thể của mỗi widget cho mỗi theme sẽ được thực hiện ở các subclass. Client code sẽ call đến các hàm được định nghĩa bởi WidgetFactory để lấy ra được widget instance nhưng nó không biết (và cũng không cần biết) class widget cụ thể nào được trả về. Chính vì vậy Client code sẽ độc lập và tách biệt khỏi implementation cụ thể của các widget, cũng hoàn toàn độc lập với việc các widget instance được tạo ra như thế nào.

Giả sử chúng ta có 2 themes là Dark và Light, 2 widgets là ScrollBar và Button. Chúng ta sẽ áp dụng Abstract Factory như sau.

*** class diagram

Có 2 class con cụ thể của WidgetFactory cho mỗi theme đó là LightThemeWidgetFactory DarkThemeWidgetFactory. Mỗi class con phải implement các method để tạo widget instance tương ứng với mỗi theme. Ví dụ: method createScrollBar() trên LightThemeWidgetFactory sẽ khởi tạo và trả về instance của scroll bar tương ứng với Light theme trong khi đó method createScrollBar() trên DarkThemeWidgetFactory phải khởi tạo và trả về scroll bar tương ứng với Dark theme. Client code tạo các widget instance chỉ thông qua interfaces được khai báo bởi WidgetFactory mà không quan tâm đến các class cụ thể của widget.

Ngoài ra các WidgetFactory cụ thể (như LightThemeWidgetFactory hoặc DarkThemeWidgetFactory) còn có tác dụng đảm bảo tính nhất quán giữa các widget. Khi LightThemeWidgetFactory được sử dụng thì scroll bar sẽ là scroll bar của Light theme, button cũng là button của Light theme, tương tự đối với DarkThemeWidgetFactory.

Sau đây chúng ta sẽ đi vào phần code demo. *** tạo interface chung cho các widgets *** implement widgets cụ thể cho theme Dark và Light *** tạo interface WidgetFactory *** implement interface WidgetFactory class LightThemeWidgetFactory → class DarkThemeWidgetFactory → *** Client code sử dụng Factory để tạo instance của Widget Sử dụng Light theme → Sử dụng Dark theme →

Chốt lại Abstract Factory nên được xem xét sử dụng khi

  • Xây dựng hệ thống hoặc ứng dụng sử dụng nhiều họ các products (products đây vd như ScrollBar, Button; còn họ ở đây vd như Dark, Light).
  • Một họ các đối tượng products liên quan đến nhau, được thiết kế để được sử dụng cùng nhau một cách nhất quán.
  • Muốn tách biệt độc lập phần code sử dụng các products với việc implement và tạo instance của chúng, nâng cao tính reusability và mantainability của hệ thống.
 

— Phạm Minh Tuấn (Shun) —