Factory Pattern là một trong những design pattern quan trọng giúp code linh hoạt, dễ mở rộng và dễ bảo trì. Trong thực tế, đặc biệt khi làm việc với Laravel, bạn gần như đang sử dụng Factory mỗi ngày mà không để ý. Bài này sẽ giúp bạn hiểu từ bản chất → cách viết → cách áp dụng chuẩn production.


Factory Pattern là gì

Factory Pattern là một design pattern dùng để:

Tách việc khởi tạo object ra khỏi nơi sử dụng object

Thay vì tạo object trực tiếp bằng new, bạn sẽ thông qua một lớp trung gian gọi là Factory.


Vấn đề khi không dùng Factory

Giả sử bạn viết code như sau:

$cache = new RedisCache();

Nhìn qua thì ổn, nhưng có vấn đề lớn

  • Code bị phụ thuộc cứng vào Redis
  • Muốn đổi sang File hoặc Memcached → sửa toàn bộ
  • Khó test (không mock được dễ dàng)
  • Logic khởi tạo nằm rải rác khắp nơi

👉 Đây gọi là tight coupling


Cách Factory Pattern giải quyết

Bạn tạo một lớp Factory:

class CacheFactory {
    public static function make() {
        return new RedisCache();
    }
}

Sử dụng:

$cache = CacheFactory::make();

Bản chất thực sự của Factory

Factory không phải chỉ là “thêm 1 lớp”

👉 Nó giải quyết 3 vấn đề cốt lõi:

Tập trung việc tạo object

Toàn bộ logic khởi tạo nằm ở một nơi duy nhất


Che giấu sự phức tạp

return new RedisCache(
    new Connection(...),
    new Logger(...),
    config(...)
);

Bên ngoài chỉ cần:

CacheFactory::make();

Cho phép thay đổi mà không ảnh hưởng code sử dụng

if ($driver === 'redis') {
    return new RedisCache();
}if ($driver === 'file') {
    return new FileCache();
}

👉 Code bên ngoài không cần sửa


Ví dụ thực tế chuẩn hơn

Interface

interface CacheInterface {
    public function get($key);
}

Implementations

class RedisCache implements CacheInterface {
    public function get($key) {
        return "redis: " . $key;
    }
}class FileCache implements CacheInterface {
    public function get($key) {
        return "file: " . $key;
    }
}

Factory

class CacheFactory {
    public static function make($driver): CacheInterface {
        return match ($driver) {
            'redis' => new RedisCache(),
            'file' => new FileCache(),
            default => throw new Exception("Driver not supported")
        };
    }
}

Sử dụng

$cache = CacheFactory::make('redis');
echo $cache->get('user');

Áp dụng trong Laravel

Trong Laravel, Factory xuất hiện ở rất nhiều nơi:


Cache driver

Cache::store('redis');
Cache::store('file');

👉 Laravel dùng Factory để tạo driver tương ứng


Database connection

DB::connection('mysql');
DB::connection('pgsql');

👉 Mỗi connection là một object khác nhau được tạo qua factory


Model Factory (fake data)

User::factory()->create();

👉 Tạo dữ liệu test mà không cần new thủ công


Khi nào nên dùng Factory

Bạn nên dùng Factory khi:

Có nhiều implementation

  • Redis, File, API...
  • MySQL, PostgreSQL...

Logic khởi tạo phức tạp

  • Có nhiều dependency
  • Có config động

Muốn giảm coupling

Không muốn code phụ thuộc class cụ thể


Muốn test dễ hơn

Có thể mock factory hoặc interface


Khi nào không cần dùng

Không phải lúc nào cũng dùng Factory

👉 Tránh khi:

  • Object đơn giản
  • Chỉ có 1 implementation
  • Không có khả năng thay đổi

So sánh nhanh

CáchĐặc điểm
new trực tiếpnhanh nhưng cứng
Factorylinh hoạt, dễ mở rộng

Factory vs Dependency Injection trong Laravel

Đây là điểm nhiều dev nhầm:

Factory

  • Bạn chủ động gọi để tạo object
CacheFactory::make('redis');

Dependency Injection (Laravel)

public function __construct(CacheInterface $cache)

👉 Laravel tự inject từ container


Khác biệt cốt lõi

  • Factory → bạn kiểm soát tạo object
  • DI Container → framework kiểm soát

Kết luận

Factory Pattern không chỉ là “tạo thêm 1 lớp”, mà là một abstraction quan trọng giúp:

  • Tách việc khởi tạo object khỏi business logic
  • Giảm phụ thuộc giữa các class
  • Dễ dàng mở rộng hệ thống
  • Hỗ trợ test và maintain tốt hơn

👉 Nếu nói theo kiểu senior:

Factory Pattern giúp centralize object creation, giảm coupling và hỗ trợ mở rộng theo nguyên lý Open Closed.