Kiến trúc hệ thống trên Laravel – phần 7
Bài đăng này đã không được cập nhật trong 8 năm
Các bài viết trong series
Kiến trúc hệ thống trên Laravel – phần 1 : Tại sao phải áp dụng architect vào trong Laravel Kiến trúc hệ thống trên Laravel – phần 2 : OOP, Interface, Dependency Injection, IoC Kiến trúc hệ thống trên Laravel – phần 3 : Phân tích sâu vào việc sử dụng interface Kiến trúc hệ thống trên Laravel – phần 4 : Design Pattern – Decorator Kiến trúc hệ thống trên Laravel – phần 5 : Design Pattern – Adapter Kiến trúc hệ thống trên Laravel – phần 6 : Design Pattern – Repository Kiến trúc hệ thống trên Laravel – phần 7 : Design Pattern – Factory Kiến trúc hệ thống trên Laravel – phần 8 : Advance component trong Laravel Kiến trúc hệ thống trên Laravel – phần 9 : Mô hình kiến trúc cụ thể Part 1 Kiến trúc hệ thống trên Laravel – phần 10 : Mô hình kiến trúc cụ thể Part 2
Xin chào các bạn. Mình vào nghề lập trình cũng đã lâu, cũng có 1 số hiểu biết coi như là nâng cao về framework Laravel. Nên hôm nay mình xin chia sẻ 1 chút về kiến trúc hệ thống của mình được xây dựng trên Laravel như thế nào. Mong rằng có thể giúp ích cho các bạn .
Hôm nay chúng ta đi vào factory nhé.
Thực ra với Factory Design Pattern chúng ta có 2 dạng (ít nhất là mình biết thế) nhưng mình sẽ diễn đạt cách mình nhìn nhận và sử dụng factory như thế nào nhé (không theo sách vở đâu >.<)
Factory là cái nhà máy sản xuất ra các object theo các điều kiện có sẵn ^^.
Đặc điểm để mình nghĩ đến factory là các điều kiện if-else / switch-case giống nhau được lặp lại ở nhiều chỗ .
Đi vào ví dụ cái cho nó dễ hiểu nhá ^^.
Các hãng máy bay bây giờ đều có kiểu đánh level khách hàng thân thiết: hạng bạch kim, hàng vàng, hạng bạc, hạng đồng … -> với mỗi hạng thì chiết khấu vé khác nhau và tặng dặm bay khác nhau -> bài toán khá cơ bản nhở, thế chúng ta thường triển khai như thế nào?
Thường thường, chúng ta sẽ có 1 object $user để trỏ tới khách hàng và $user->type chỉ hạng khách hàng. If-else / hoặc switch-case để check điều kiện là xong:
<?php
class OrderController
{
/**
* Store price information when booking ticket
*/
public function calculatePrice()
{
// Xử lý chính phần mình muốn nói ở đây, các phần khác bỏ qua nhá ...
$type = $user->type;
switch ($type) {
case 'bach kim':
$chiet_khau = 0.1;
break;
case 'vang':
$chiet_khau = 0.07;
break;
case 'bac':
$chiet_khau = 0.05;
break;
case 'dong':
$chiet_khau = 0.03;
break;
}
$price = $base_price - $base_price * $chiet_khau;
}
public function calculateBonus()
{
// Xử lý chính phần mình muốn nói ở đây, các phần khác bỏ qua nhá ...
$type = $user->type;
switch ($type) {
case 'bach kim':
$bonus = 1000;
break;
case 'vang':
$bonus = 700;
break;
case 'bac':
$bonus = 500;
break;
case 'dong':
$bonus = 300;
break;
}
}
}
Các bạn thấy đấy, 1 chuỗi điều kiện sẽ được lặp đi lặp lại ở rất nhiều chỗ dẫn đến việc follow logic của bài toán rất khó (ví dụ để kiếm tất cả benefit mà ông bạch kim nhận được thì phải tra rất nhiều controller khác nhau) -> nếu gom nhóm được những xử lý theo từng hạng thì việc quản lý logic, chỉnh sửa hệ thống về sau sẽ dễ dàng hơn rất nhiều rồi.
Làm như thế nào
1. Tập trung xử lý phần điều kiện trong if-else vào các object riêng biệt
Class BachKim
{
public function discount()
{
return 0.1;
}
public function bonus()
{
return 1000;
}
}
Class Vang
{
public function discount()
{
return 0.07;
}
public function bonus()
{
return 700;
}
}
Class Bac
{
public function discount()
{
return 0.05;
}
public function bonus()
{
return 500;
}
}
Class Dong
{
public function discount()
{
return 0.03;
}
public function bonus()
{
return 300;
}
}
2. Sinh object theo điều kiện đầu vào
class UserFactory
{
public static function make($type)
{
switch ($type) {
case 'bach kim':
return new BachKim;
break;
case 'vang':
return new Vang;
break;
case 'bac':
return new Bac;
break;
case 'dong':
return new Dong;
break;
}
}
}
3. Sử dụng trong client
class OrderController
{
/**
* Store price information when booking ticket
*/
public function calculatePrice()
{
// Xử lý chính phần mình muốn nói ở đây, các phần khác bỏ qua nhá ...
$type = $user->type;
$customer = UserFactory::make($type);
$base_price = $customer->discount();
$price = $base_price - $base_price * $chiet_khau;
}
public function calculateBonus()
{
// Xử lý chính phần mình muốn nói ở đây, các phần khác bỏ qua nhá ...
$type = $user->type;
$customer = UserFactory::make($type);
$bonus = $customer->bonus();
}
}
Vâng, rất nhiều class mới được sinh ra để xử lý 1 vấn đề khá là đơn giản (và thường gặp nữa), nhưng bù lại thì logic của các phần đã được tập trung lại, việc xem xét, chỉnh sửa logic của bài toán bây giờ đơn giản hơn rất nhiều.
Xin cám ơn các bạn đã kiên nhẫn follow mình trong series này .
All rights reserved
Bình luận
@trthanhbk Cảm ơn anh vì những bài viết hữu ích về Laravel (y) Em có một góp ý nhỏ là ở mỗi bài viết trong series, anh thêm vào mục index về danh sách và đường link dẫn đến các bài viết, kiểu như
Index
Kiến trúc hệ thống trên Laravel – phần 1 Kiến trúc hệ thống trên Laravel – phần 2 Kiến trúc hệ thống trên Laravel – phần 3 Kiến trúc hệ thống trên Laravel – phần 4 Kiến trúc hệ thống trên Laravel – phần 5 Kiến trúc hệ thống trên Laravel – phần 6 Kiến trúc hệ thống trên Laravel – phần 7
thì người đọc mới vào sẽ dễ dàng tiếp cận được với những bài viết cũ trong series của anh hơn ạ
Đã làm theo gợi ý của bạn ^^. Xin cám ơn bạn rất nhiều
cái ví dụ cuối cùng, không phải trường hợp nào cũng áp dụng được, cái này chỉ áp dụng được khi mình có nhu cầu thêm mới các "type (hạng khách hàng)", còn nếu muốn thêm mới "hành vi" sẽ phải sửa lại code của toàn bộ các class
btw, ra nốt bài cuối của series luôn đi anh
Ha ha, còn 1 vài bài nữa mới hết series nên chưa đến bài cuối luôn được đâu ^^. Còn vấn đề thêm mới "hành vi" thì thà rằng chúng ta thêm mới ở các class được tập trung trong 1 folder sẽ dễ dàng hơn rất nhiều là phải thêm mới ở các file khác nhau trong hệ thống ^^
quan điểm cá nhân của em thôi nhé: Trong cách làm dựa vào Factory DP như trong Ví dụ này :
Bây giờ đặt trường hợp em muốn thêm hành vi gift() cho các loại customer, anh sẽ phải viết thêm vào toàn bộ các Class Customer => không tốt Và nếu em muốn thêm loại Customer Standard thì em phải sửa lại Factory => cũng không tốt
thay vì thế nếu em không dùng Factory, mà chỉ sử dụng 1 datastructure như Code ban đầu của anh lúc chưa dùng Factory, thì khi em muốn thêm 1 behavior thì rất đơn giản đổi lại việc thêm 1 Type của Customer thì khó khăn hơn vì phải sửa lại Code cũ. 1 vấn đề có tính 2 chiều, đều có lợi và có hại.
Nói chung là theo em nên tuỳ từng trường hợp, ý đồ của mình phát triển theo hướng nào thì mới áp dụng các DP vào nữa
Mình đồng ý với bạn
. Một vấn đề luôn có nhiều cách giải quyết, không phải lúc nào cũng nhất nhất phải design pattern ^^. Design pattern bản chất sinh ra để giải quyết 1 số vấn đề nhất định nên nó không phải là cái gì đó là hoàn hảo ^^. Điều quan trọng khi học design pattern là nhận diện được vấn đề để áp dụng được nó vào -> đừng nên thần tượng hóa design pattern nhưng cũng không được xem nhẹ nó nha >.<