Factory Pattern nhà máy tạo thực thể
NỘI DUNG BÀI VIẾT
1. Factory Pattern là gì?
Tần suất sử dụng: 5/5, Factory Pattern được sử dụng cực nhiều trong lập trình. Factory pattern cũng như Singleton pattern thuộc về dạng Creational Design Pattern, tuy nhiên có một chút khác biệt. Singleton pattern áp dụng để tạo và quản lý một đối tượng duy nhất của một class trong khi Factory pattern được sử dụng để có thể tạo ra nhiều đối tượng khác nhau từ nhiều class.
Tại sao cần Factory pattern trong khi chúng ta có thể tạo được đối tượng từ Singleton Pattern? Vấn đề là ở chỗ đôi khi chúng ta không biết trước được là muốn tạo đối tượng từ class cụ thể nào, do đó việc chỉ định class để tạo ra đối tượng cần phải được gán động. Sơ đồ UML của Factory Pattern như sau:

Factory pattern cần sử dụng một lớp trìu tượng (abstract class) và có một phương thức static, được quy ước tên là Create(), factory(), factoryMethod() hoặc createInstance(). Phương thức này có một tham số để nhận biết dạng đối tượng cần tạo và trả về đối tượng này.
static function Create($type) {
// Kiểm tra tham số $type và tạo đối tượng từ class tương ứng để trả về.
return new SomeClassType();
}
Code language: PHP (php)
2. Ví dụ về Factory Pattern trong PHP
Có vẻ chưa hiểu lắm, không sao, ví dụ tiếp theo sẽ giúp bạn hiểu ngay thôi. Chúng ta quay trở lại ví dụ về các hình chữ nhật (rectangle), hình tam giác (triangle). Chúng ta tạo ra file factory.php trong thư mục oop\pattern với nội dung như sau:
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Ví dụ về Factory pattern</title>
</head>
<body>
<?php
#------------ ĐỊNH NGHĨA CLASS ----------------------#
/* Định nghĩa class ShapeFactory sử dụng Factory pattern
* The class contains no attributes.
* The class contains one method: Create().
*/
abstract class ShapeFactory {
// Phương thức static để tạo đối tượng
static function Create($type, array $sizes) {
// Xác định dạng đối tượng theo tham số nhận vào
switch ($type) {
case 'rectangle':
return new Rectangle($sizes[0], $sizes[1]);
break;
case 'triangle':
return new Triangle($sizes[0], $sizes[1], $sizes[2]);
break;
}
}
}
/* Định nghĩa lớp trìu tượng Shape
* Lớp Shape không có thuộc tính
* Lớp Shape có 2 phương thức trìu tượng:
* - getArea()
* - getPerimeter()
*/
abstract class Shape {
abstract protected function getArea();
abstract protected function getPerimeter();
}
/* Định nghĩa lớp Triangle
* Lớp Triangle có 2 thuộc tính:
* - private $_sides (array)
* - private $_perimeter (number)
* Lớp Triangle có 3 phương thức:
* - _ _construct()
* - getArea()
* - getPerimeter()
*/
class Triangle extends Shape {
private $_sides = array();
private $_perimeter = NULL;
function __construct($s0 = 0, $s1 = 0, $s2 = 0) {
$this->_sides[] = $s0;
$this->_sides[] = $s1;
$this->_sides[] = $s2;
// Tính toán và thiết lập chu vi hình tam giác
$this->_perimeter = array_sum($this->_sides);
}
// Phương thức tính diện tích hình tam giác từ chu vi và các cạnh
public function getArea() {
return (SQRT(($this->_perimeter/2) * (($this->_perimeter/2) - $this->_sides[0]) * (($this->_perimeter/2) - $this->_sides[1]) * (($this->_perimeter/2) - $this->_sides[2])));
}
// Phương thức lấy chu vi hình tam giác
public function getPerimeter() {
return $this->_perimeter;
}
}
/* Định nghĩa class Rectangle
* Các thuộc tính của class: width(chiều rộng), height(chiều cao).
* Các phương thức của lớp:
* - setSize()
* - getArea()
* - getPerimeter()
* - isSquare()
*/
class Rectangle {
// Khai báo các thuộc tính
public $width = 0;
public $height = 0;
// Hàm khởi tạo
function __construct($w = 0, $h = 0) {
$this->width = $w;
$this->height = $h;
}
// Phương thức này thiết lập các kích thước của hình chữ nhật
function setSize($w = 0, $h = 0) {
$this->width = $w;
$this->height = $h;
}
// Phương thức này tính diện tích hình chữ nhật
function getArea() {
return ($this->width * $this->height);
}
// Phương thức này tính chu vi hình chữ nhật
function getPerimeter() {
return ( ($this->width + $this->height) * 2 );
}
// Phương thức này kiểm tra xem hình chữ nhật này có phải là hình vuông
function isSquare() {
if ($this->width == $this->height) {
return true; // Hình chữ nhật
} else {
return false; // Không phải hình chữ nhật
}
}
}
#------------ KẾT THÚC ĐỊNH NGHĨA CLASS ----------------------#
if (isset($_GET['shape'], $_GET['dimensions'])) {
// Tạo ra một đối tượng từ với thông số từ query string
$obj = ShapeFactory::Create($_GET['s'], $_GET['d']);
echo "<h2>Tạo ra hình {$_GET['shape']}:</h2>";
echo '<p>Diện tích hình: ' . $obj->getArea() . '</p>';
echo '<p>Chu vi hình: ' . $obj->getPerimeter() . '</p>';
} else {
echo '<p>Cần cung cấp hình dạng và kích thước!</p>';
}
// Xóa đối tượng
unset($obj);
?>
</body>
</html>
Code language: HTML, XML (xml)
Trong ví dụ này chúng ta đã tạo ra lớp ShapeFactory áp dụng Factory Pattern, nó sẽ nhận các tham số đầu vào để tạo ra một thực thể tương ứng với class cần tạo. Tiếp đó là các class Rectangle (hình chữ nhật) và Triangle (hình tam giác) được mở rộng từ lớp trìu tượng Shape (hình). Tiếp đó, chúng ta sử dụng các query string của URL để đưa vào dạng hình và kích thước hình cần tạo:
- s – shape là hình cần tạo: rectangle hoặc triangle
- d – dimension là kích thước của hình, nếu là tam giác nhập 3 cạnh, nếu là hình chữ nhật nhập 2 cạnh.Thực hiện chạy thử http://oop.dev/factory.php?s=rectangle&d[\]=10&d[\]=20 chúng ta được kết quả:

Tiếp theo thử tạo hình tam giác với các cạnh là 5, 10, 15 với URL http://oop.dev/factory.php?s=triangle&d[\]=5&d[\]=10&d[\]=15 kết quả như sau:

Như vậy với việc áp dụng Factory Pattern chúng ta đã tạo được các đối tượng khác nhau tùy thuộc vào từng tình huống mà không biết trước được như Singleton Pattern.
3. Lời kết
Factory Pattern rất hữu dụng trong việc tạo ra các đối tượng động, ứng dụng sẽ mềm dẻo hơn với việc áp dụng Factory Pattern. Để sử dụng các Design Pattern hiệu quả bạn nên tìm hiểu tất cả các mẫu lập trình trong Create Design Pattern để tìm được pattern phù hợp nhất cho việc tạo đối tượng.
Cảm ơn các bạn đã đọc.
Các bạn có thể tham khảo các bài viết hay về Laravel tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.
Nguồn tham khảo: Allaravel
Leave a Reply