Factory Method là một trong những Creational Design Patterns (mẫu thiết kế khởi tạo), giúp bạn tạo đối tượng mà không cần chỉ định lớp cụ thể. Thay vì khởi tạo đối tượng trực tiếp bằng từ khóa new
, Factory Method sử dụng một phương thức được định nghĩa trong một lớp hoặc interface để quyết định lớp nào sẽ được khởi tạo.
Mục đích
- Cung cấp sự linh hoạt khi thêm các lớp mới mà không cần thay đổi mã nguồn hiện tại.
- Tăng khả năng mở rộng (Open/Closed Principle).
- Tách biệt logic khởi tạo đối tượng ra khỏi logic nghiệp vụ chính
Cấu trúc Factory Method
- Product (Sản phẩm): Interface hoặc lớp trừu tượng định nghĩa các đối tượng được tạo.
- Concrete Product (Sản phẩm cụ thể): Các lớp cụ thể triển khai hoặc mở rộng từ
Product
. - Creator (Người tạo): Lớp hoặc interface định nghĩa phương thức
factoryMethod
trả vềProduct
. - Concrete Creator (Người tạo cụ thể): Lớp cụ thể triển khai phương thức
factoryMethod
để tạo raConcrete Product
.
Ví dụ minh họa: Factory Method trong PHP
1. Khởi tạo lớp sản phẩm
<?php interface Logger { public function log(string $message): void; } class FileLogger implements Logger { public function log(string $message): void { echo "Logging to a file: $message"; } } class DatabaseLogger implements Logger { public function log(string $message): void { echo "Logging to a database: $message"; } } ?>
2. Lớp Creator và Concrete Creator
<?php abstract class LoggerFactory { // Phương thức factory method abstract public function createLogger(): Logger; public function log(string $message): void { $logger = $this->createLogger(); $logger->log($message); } } class FileLoggerFactory extends LoggerFactory { public function createLogger(): Logger { return new FileLogger(); } } class DatabaseLoggerFactory extends LoggerFactory { public function createLogger(): Logger { return new DatabaseLogger(); } } ?>
3. Sử dụng Factory Method
<?php // Tạo logger dạng File $fileLoggerFactory = new FileLoggerFactory(); $fileLoggerFactory->log("This is a file log message."); // Tạo logger dạng Database $databaseLoggerFactory = new DatabaseLoggerFactory(); $databaseLoggerFactory->log("This is a database log message."); ?>
Ưu điểm của Factory Method
- Dễ mở rộng: Thêm loại sản phẩm mới chỉ cần tạo một lớp sản phẩm và một lớp creator tương ứng.
- Tái sử dụng: Cùng một logic nghiệp vụ, nhưng các đối tượng khác nhau có thể được tạo ra mà không sửa đổi mã nguồn chính.
- Tách biệt trách nhiệm: Creator chịu trách nhiệm khởi tạo đối tượng, logic khởi tạo không nằm trong logic nghiệp vụ.
Nhược điểm
- Code phức tạp hơn vì cần thêm các lớp Creator và Concrete Product.
- Khả năng mở rộng đòi hỏi thiết kế ban đầu phải chuẩn xác.
Factory Method phù hợp trong các trường hợp cần khởi tạo nhiều loại đối tượng khác nhau nhưng muốn giấu đi sự phức tạp của việc tạo đối tượng cụ thể.
Dưới đây là ví dụ về việc sử dụng Factory Method để tạo CRUD cho User
và Role
trong PHP. Mỗi loại (User và Role) sẽ được quản lý thông qua một Factory riêng, giúp code linh hoạt và dễ mở rộng
Example 2:
1. Định nghĩa Interface chung
<?php interface CrudInterface { public function create(array $data): void; public function read(int $id): ?array; public function update(int $id, array $data): void; public function delete(int $id): void; } ?>
2. Tạo lớp Product (User và Role)
<?php class User implements CrudInterface { private $users = []; // Mô phỏng cơ sở dữ liệu public function create(array $data): void { $this->users[$data['id']] = $data; echo "User created: " . json_encode($data) . PHP_EOL; } public function read(int $id): ?array { return $this->users[$id] ?? null; } public function update(int $id, array $data): void { if (isset($this->users[$id])) { $this->users[$id] = array_merge($this->users[$id], $data); echo "User updated: " . json_encode($this->users[$id]) . PHP_EOL; } else { echo "User not found." . PHP_EOL; } } public function delete(int $id): void { unset($this->users[$id]); echo "User deleted: $id" . PHP_EOL; } } class Role implements CrudInterface { private $roles = []; // Mô phỏng cơ sở dữ liệu public function create(array $data): void { $this->roles[$data['id']] = $data; echo "Role created: " . json_encode($data) . PHP_EOL; } public function read(int $id): ?array { return $this->roles[$id] ?? null; } public function update(int $id, array $data): void { if (isset($this->roles[$id])) { $this->roles[$id] = array_merge($this->roles[$id], $data); echo "Role updated: " . json_encode($this->roles[$id]) . PHP_EOL; } else { echo "Role not found." . PHP_EOL; } } public function delete(int $id): void { unset($this->roles[$id]); echo "Role deleted: $id" . PHP_EOL; } } ?>
3. Tạo lớp Factory
<?php abstract class CrudFactory { abstract public function createCrud(): CrudInterface; } class UserFactory extends CrudFactory { public function createCrud(): CrudInterface { return new User(); } } class RoleFactory extends CrudFactory { public function createCrud(): CrudInterface { return new Role(); } } ?>
4. Sử dụng Factory Method cho CRUD
<?php // Factory cho User $userFactory = new UserFactory(); $userCrud = $userFactory->createCrud(); // Thực hiện các thao tác CRUD cho User $userCrud->create(['id' => 1, 'name' => 'Hoa Dev', 'email' => 'hoadev@example.com']); $userCrud->update(1, ['email' => 'hoadev.code@example.com']); $userData = $userCrud->read(1); echo "Read User: " . json_encode($userData) . PHP_EOL; $userCrud->delete(1); // Factory cho Role $roleFactory = new RoleFactory(); $roleCrud = $roleFactory->createCrud(); // Thực hiện các thao tác CRUD cho Role $roleCrud->create(['id' => 101, 'name' => 'Admin']); $roleCrud->update(101, ['name' => 'Super Admin']); $roleData = $roleCrud->read(101); echo "Read Role: " . json_encode($roleData) . PHP_EOL; $roleCrud->delete(101); ?>
Kết quả
Khi chạy đoạn code trên, kết quả sẽ hiển thị như sau:
User created: {"id":1,"name":"Hoa Dev","email":"hoadev@example.com"} User updated: {"id":1,"name":"Hoa Dev","email":"hoadev.code@example.com"} Read User: {"id":1,"name":"Hoa Dev","email":"hoadev.code@example.com"} User deleted: 1 Role created: {"id":101,"name":"Admin"} Role updated: {"id":101,"name":"Super Admin"} Read Role: {"id":101,"name":"Super Admin"} Role deleted: 101
Ưu điểm
- Tách biệt logic CRUD: Mỗi Factory chịu trách nhiệm tạo ra một loại đối tượng cụ thể (
User
hoặcRole
). - Dễ mở rộng: Có thể dễ dàng thêm các thực thể mới (ví dụ:
Permission
) bằng cách tạo lớp Product và Factory tương ứng. - Tăng tính tái sử dụng: Logic CRUD chung được định nghĩa trong interface, đảm bảo mọi đối tượng tuân theo một chuẩn.
Bạn có thể áp dụng mô hình này để quản lý nhiều loại đối tượng khác nhau trong ứng dụng của mình.