Facade design pattern :(
https://viblo.asia/p/tap-8-facade-laravel-bWrZnwnrlxw
Xem thêm: https://allaravel.com/blog/facade-pattern-don-gian-hoa-trong-viet-code-php
III. Laravel facade hoạt động như thế nào? (How Laravel facade work)
Trước tiên ta hãy cùng tìm hiểu một chút về facade design pattern.
1. Facade design pattern
Về khái niệm, facade design pattern hiểu đơn giản là nó sẽ giúp coder giao tiếp với source code xử lý phức tạp một cách dễ dàng thông qua một class facade trung gian.
Giả sử mình có thông tin của một user bao gồm tên, địa chỉ và học vấn. Trong đó:
Tên gồm: họ tên và tên đăng nhập
Địa chỉ: số nhà, đường, thành phố
Học vấn: trường cấp 1, 2, 3 và đại học
Đây là file data của user đó:
<?php
return [
'name' => [
'fullname' => 'Nguyễn Văn A',
'username' => 'anguyen'
],
'address' => [
'number_house' => '89',
'street' => 'Lê Lợi',
'city' => 'Hồ Chí Minh'
],
'education' => [
'primary_school' => 'Nguyễn Trãi',
'junior_high_school' => 'Lê Hồng Phong',
'high_school' => 'Lê Lợi',
'university' => 'Sài Gòn'
]
];
Yêu cầu: Lấy tất cả thông tin user.
Nếu làm theo cách thông thường thì bạn sẽ có thể xử lý theo hai hướng:
Xử lý tất cả các yêu cầu trên trong một class
User
.Tạo ra 3 class con
NameUser
,AddressUser
vàEducationUser
để xử lý từng yêu cầu.
Đối với cách 1, mình sẽ tạo thêm file User.php
, ta sẽ có cấu trúc thư mục như sau:
myproject/
| autoload.php
| data.php
| User.php
| index.php
Các bạn quan sát nội dung file User.php
mình code để thực hiện yêu cầu trên:
<?php
class User
{
protected $user, $name, $address, $education;
public function __construct()
{
$this->user = require_once 'data.php';
$this->name = $this->user['name'];
$this->address = $this->user['address'];
$this->education = $this->user['education'];
}
public function getFullname()
{
return $this->name['fullname'];
}
public function getUsername()
{
return $this->name['username'];
}
public function getNumberHouse()
{
return $this->address['number_house'];
}
public function getStreet()
{
return $this->address['street'];
}
public function getCity()
{
return $this->address['city'];
}
public function getPrimarySchool()
{
return $this->education['primary_school'];
}
public function getJuniorHighSchool()
{
return $this->education['junior_high_school'];
}
public function getHighSchool()
{
return $this->education['high_school'];
}
public function getUniversity()
{
return $this->education['university'];
}
}
Đầu tiên ta nói về ưu điểm của cách 1. Nếu xử lý tất cả trong một class User
thì sẽ giúp cho coder dễ dàng lấy thông tin user một cách nhanh chóng.
<?php
require_once 'autoload.php';
$user = new User;
$user->getFullname();
$user->getCity();
//
Nhưng xét về nhược điểm, bạn có thể nhận ra rằng file User.php
quá dài, xử lý nhiều trường khác nhau. Nếu đây không phải là một ví dụ nhỏ dễ hình dung mà là một project quy mô vừa thôi thì cũng đủ làm ta thấy choáng ngợp nếu nhồi nhét các phương thức, thuộc tính vào một class như vậy.
Nhận xét cách 1:
Ưu điểm: cung cấp cú pháp dễ nhớ, nhanh chóng
Nhược điểm: class xử lý quá nhiều, khó bảo trì, nâng cấp.
⇒ Chưa thuyết phục.
Đối với cách 2, ta sẽ có cấu trúc thư mục như sau:
myproject/
| autoload.php
| data.php
| NameUser.php
| AddressUser.php
| EducationUser.php
| index.php
Nội dung mỗi class NameUser.php
, AddressUser.php
và EducationUser.php
lần lượt là:
<?php
class NameUser
{
protected $name;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['name'];
}
public function getFullname()
{
return $this->name['fullname'];
}
public function getUsername()
{
return $this->name['username'];
}
}
<?php
class AddressUser
{
protected $address;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['address'];
}
public function getNumberHouse()
{
return $this->address['number_house'];
}
public function getStreet()
{
return $this->address['street'];
}
public function getCity()
{
return $this->address['city'];
}
}
<?php
class EducationUser
{
protected $education;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['address'];
}
public function getPrimarySchool()
{
return $this->education['primary_school'];
}
public function getJuniorHighSchool()
{
return $this->education['junior_high_school'];
}
public function getHighSchool()
{
return $this->education['high_school'];
}
public function getUniversity()
{
return $this->education['university'];
}
}
Đối với cách này, ưu điểm của nó là đã phân chia các yêu cầu nhỏ thành từng class riêng, có tính tách rời, dễ dàng xử lý khi có vấn đề.
Nhưng với cách 2, ta lại gặp phải một nhược điểm đó chính là về cách thức lấy thông tin user.
<?php
require_once 'autoload.php';
$nameUser = new NameUser;
$addressUser = new AddressUser;
$educationUser = new Education;
$nameUser->getUsername();
$addressUser->getCity();
$educationUser->getHighSchool();
//
Ở đây chỉ có 3 class nên có thể bạn nghĩ sẽ nhớ được, không trở ngại. Nhưng bạn thử tưởng tượng nếu ứng dụng bạn "phình to" ra, class xử lý khởi tạo thêm rất nhiều, việc nhớ tên từng class với nhiệm vụ của nó thật sự là một vấn đề đáng quan tâm.
Nhận xét cách 2:
Ưu điểm: phân chia công việc cho từng class, dễ dàng bảo trì, nâng cấp
Nhược điểm: khó khăn trong việc nhớ cú pháp, tên và nhiệm vụ của từng class
⇒ Chưa thuyết phục.
Nhưng với facade design pattern, nó sẽ lấy ưu điểm của cả hai cách trên để tạo ra hiệu năng tuyệt vời.
Ưu điểm facade design pattern:
Phân chia công việc cho từng class, dễ dàng bảo trì, nâng cấp
Cung cấp cú pháp dễ nhớ, nhanh chóng
Để đáp ứng yêu cầu trên với facade design patter, chúng ta có cấu trúc thư mục như sau:
myproject/
| autoload.php
| data.php
| User.php
├── Facades/
| | NameUser.php
| | AddressUser.php
| | Education.php
| index.php
Ba file trong thư mục Facades
thì mình sẽ giữ nguyên nội dung như ở cách 2, chỉ thêm namespace ở đầu file:
namespace Facades;
Bây giờ mình chỉ viết xử lý ở file User.php
.
<?php
use Facades\NameUser;
use Facades\AddressUser;
use Facades\EducationUser;
class User
{
protected $name, $address, $education
public function __construct()
{
$this->name = new NameUser;
$this->address = new AddressUser;
$this->education = new EducationUser;
}
public function __call($method, $parameters)
{
if (method_exists($this->name, $method) == true) {
return $this->name->{$method}();
}
if (method_exists($this->address, $method) == true) {
return $this->name->{$method}();
}
if (method_exists($this->education, $method) == true) {
return $this->name->{$method}();
}
}
}
Nếu đã học PHP OOP, chắc bạn cũng đã quá quen thuộc với magic-method __call
rồi. Method __call
sẽ được thực thi khi class object chứa nó gọi một method nào đó không tồn tại. Sau đó mình sẽ kiểm tra xem method đó thuộc object nào trong 3 class object đã set ở __construct
rồi thực thi nó. Như vậy bạn thấy, bây giờ User
đóng vai trò là một facade, vừa đạt được hiệu năng, vừa tối ưu được cú pháp.
<?php
require_once 'autoload.php';
$user = new User;
$user->getFullname();
$user->getCity();
//
Dưới đây là mô hình hoạt động của facade design pattern:
Last updated
Was this helpful?