Factory method design pattern

68

Factory method là một design pattern thuộc nhóm khởi tạo, nó đóng vai trò như một nhà máy giúp bạn tạo ra đối tượng mong muốn.

1. Bài toán

Hãy tưởng tượng, bạn đang làm tính năng đăng nhập bằng tài khoản mạng xã hội cho website. Thời gian đầu, bạn thống kê được khách hàng trên website của bạn 100% đều sử dụng facebook, vậy là bạn tích hợp tính năng đăng nhập bằng facebook cho website. Mọi logic đăng nhập bằng facebook được bạn đặt trong lớp Facebook.

Sau một thời gian hoạt động, website của bạn phát triển và được nhiều người biết đến hơn. Lúc này có một số người không sử dụng facebook mà lại sử dụng twitter, để giúp họ có thể đăng nhập được vào website của bạn, bạn quyết định làm thêm tính năng “Đăng nhập bằng twitter” bên cạnh tính năng đăng nhập bằng facebook đã có. Mọi logic đăng nhập bằng twitter được bạn đặt trong lớp Twitter.

Việc website của bạn có nhiều người biết đến hơn là một tin tốt, thế nhưng code thế nào bây giờ? Thời điểm hiện tại logic đăng nhập bằng facebook của bạn đã gắn với rất nhiều chỗ, nếu bây giờ chỉnh sửa để có thể đăng nhập được với twitter thì sẽ phải sửa lại tất cả. Về nguyên tắc, sửa gì thì sẽ phải kiểm tra lại phần đó. Vậy nếu bạn tích hợp xong twitter, thì bạn sẽ phải kiểm tra lại cả twitter và facebook. Rồi lỡ mai này tích hợp thêm github, thêm google thì bạn cũng sẽ phải kiểm tra lại hết sao?

Có vẻ không ổn, phải có cách nào đó khoa học hơn.

2. Giải pháp với Factory Method Pattern

Để giải quyết vấn đề trên, chúng ta sẽ sử dụng tới Factory Method Pattern (gọi tắt là Factory).

Factory có nghĩa là “nhà máy”, đóng vai trò như một thành phần trung gian có nhiệm vụ khởi tạo các lớp khác nhau tùy vào ngữ cảnh của bài toán.

Cụ thể với bài toán trên, mình sẽ tạo ra một lớp mới là SocialNetwork, có một static method là driver($type). Dựa vào tham số $type, mình sẽ khởi tạo ra một đối tượng tương ứng.

<?php

class Facebook
{
    public function login() { ... }
}

class Twitter
{
    public function login() { ... }
}

class SocialNetwork
{
    public static function driver($type)
    {
        if ($type == 'facebook') {
            return new Facebook();
        } else if ($type == 'twitter') {
            return new Twitter();
        }
    }
}

// Sử dụng

$type =='facebook';
$netWork = SocialNetwork::driver($type); // $netWork là đối tượng của lớp Facebook
$network->login(); // đăng nhập bằng facebook

$type = 'twitter';
$netWork = SocialNetwork::driver($type); // $netWork là đối tượng của lớp Twitter
$network->login(); // đăng nhập bằng twitter

Có một vấn đề nhỏ, là mỗi $type khác nhau lại trả về một đối tượng khác nhau, vì thế không có gì để đảm bảo rằng method login() sẽ tồn tại ở tất cả các đối tượng. Để giải quyết vấn đề này, chúng ta sẽ cho 2 class FacebookTwitter cùng implements một interface có là SocicalNetworkLogin có một method là login() chẳng hạn.

Code được chỉnh sửa lại như sau:

<?php

interface SocicalNetworkLogin
{
    public function login();
}

class Facebook implements SocicalNetworkLogin
{
    public function login() { ... }
}

class Twitter implements SocicalNetworkLogin
{
    public function login() { ... }
}

Với cách tổ chức code theo Factory method pattern, logic để khởi tạo đối tượng mới được tập trung một chỗ, và luôn sẵn sàng để bạn bổ sung thêm các logic khác mà không làm ảnh hưởng nhiều tới các logic đã có.

3. Thành phần của Factory Method Pattern

Qua bài toán và giải pháp trên, chúng ta thấy rằng Factory Method Pattern bao gồm các thành phần:

  • Client: Là nơi sử dụng Pattern, chính là đoạn $netWork = SocialNetwork::driver($type).
  • Các subclass (class con): Chính là class Facebook, Twitter.
  • Factory: Chính là SocialNetwork::driver()
  • Một interface định nghĩa các method chung để các subclass có thể implements: Chính là interface SocicalNetworkLogin. Trên thực tế, nếu đảm bảo các subclass đều có những method tương tự nhau, thì bạn có thể không cần thành phần interface này.

Với Factory Method, logic khởi tạo đối tượng được đưa từ Client sang Factory để việc quản lý trở nên tập trung hơn, cũng như dễ dàng mở rộng code hơn.

4. Khi nào thì sử dụng Factory Method Pattern

  • Khi bạn có một nhóm các class tương tự nhau, và tùy vào ngữ cảnh của bài toán mà sẽ phải khởi tạo một đối tượng từ một trong số các class trên.
  • Khi bạn cho rằng khả năng cao trong tương lai sẽ có những class tương tự nhau được tạo ra.

5. Lời kết

Như vậy mình đã giới thiệu xong với bạn design pattern đầu tiên trong loạt bài viết về design pattern. Hy vọng sẽ giúp ích cho các bạn.

Hẹn gặp lại trong các bài viết lần sau.