[Laravel 5.4][Features] HigherOrderMessaging trong laravel

Spread the love

Mở đầu :

Laravel 5.4 đã release khá lâu, và trong list bài này, giới thiệu về một tính năng (cũng như là một Design Patterns mà laravel vừa đưa vào), đó là Higher Order Messages (HOM)

HigherOrderMessages là gì  : 

Đây là bài post mà Higher Order Message lấy ý tưởng. Chúng ta sẽ thử cùng phân tích Use case để sử dụng High Order Message.

Bài toán

Nhật Bản là một nước có dân số già, ở đó, những người về hưu (class Claimnant ) sẽ được nhận tiền từ chính phủ Nhật. Mỗi Claimnant sẽ có các thuộc tính name , gender , age và benefit . Trong đó benefit thể hiện số tiền mà ngừoi nghỉ hưu sẽ được nhận từ chính phủ. Giả sử mỗi tháng, chính phủ sẽ trả cho mỗi ngừời đã nghỉ hưu số tiền là 5,000 yên, (trong đó định nghĩa, người nghỉ hưu là người có số tuổi > 59)

Giả sử nếu viết theo kiểu tuần tự thì


foreach ($claimnants as $claimnant ){
    if ($claimnant->age > 59 ){
        $claimnant->receiveBenefit(5000);
    }
}

Đây là cách tiếp cận phổ thông nhất. Nhưng nếu chúng ta refactor code bằng Laravel Collection, ta sẽ có được đoạn code bên dưới


collect($claimnants).filter(function($element){
                     //select only the claimnant that age is larger than 59
                     return $element->age > 59;
                  }).each(function($element){
                     //allow this claimnant to receive the 
                     $element.receiveBenefit(5000);
   });
}

Tuyệt vời, tuy nhiên nếu chúng ta áp dụng higher order messaging ta sẽ có viết theo như bên dưới


collect($claimnants)->filter->retired->each->receiveBenefit(5000)

Theo như định nghĩa về Higher Order Messages

Một higher order message là một message mà lấy một message khác như là một tham số. Nó định nghĩa cách mà một message được chuyển tiếp cho một hoặc nhiều objects và cách mà một responses được đối chiếu và trả về sender. Một higher order message có thể thực hiện một query hoặc update tất cả đối tượng trong collection.

Đọc xong đoạn trên, chắc hẳn chưa ai hiểu đoạn đấy nói gì… Thôi chúng ta thử dịch đoạn code đó ra tiếng Việt rồi phân tích xem sao 🙂

Người    -> thoả   -> điều kiện về hưu -> mỗi người -> nhận tiền hưu

Bằng tiếng Anh nhé

claimnant-> filter ->is_retired        ->each       ->receiveBenefit
Đoạn code ngay phía trên có vẻ gần gũi với tiếng Anh nhất, đây là một trong những lợi điểm của Higher Ordering Messages (như đã được đề cập )

Đoạn code mà sử dụng Higher Order Messages diễn tả được nghiệp vụ thực thi một cách gần nhất. Nó diễn tả cái gì được thực hiện và ẩn đi cách thực hiện nó như thế nào

Có thể bạn đã hiểu mục đích của Higher Ordering Message là làm cho những đoạn code gần gũi hơn với business thay vì là một mớ spaghetti loằng ngoằng với những closure và callbacks. Tất nhiên làm gì cũng phải trả giá. Vậy thì chúng ta cùng đi tiếp đến phàn trả giá 😀

Bất cập của Higher Order Messaging

Giả sử bài toán của chúng ta thay đổi, theo luật pháp hiện hành của nhà nước Việt Nam

  • Nam giới trên 60 tuổi : tính là hưu
  • Nữ giới trên 55 tuổi : tính là hưu

Nếu vẫn muốn sử dụng Higher Ordering Message thì , chúng ta phải viết thêm hàm cho class Claimnant kiểu như bên dưới

 


function isRetired(){
    return $this->sex == 1 && $this->age > 60 ||
           $this->sex == 0 && $this->age > 59;
}

Nếu sửa theo như thế này thì nếu sau này logic có thay đổi, hoặc điều kiện filter để nhận lương hưu thay đổi hoàn toàn, khả năng chúng ta sẽ phải sửa code (hoặc thêm hàm ) vào trong class Claimnant là rất cao, và việc code của presentation logic và application logic được trộn lẫn với nhau thì không phải là một thiết kế tốt.

Mặt khác nếu chúng ta cần phải lấy report lương hưu của từng người chúng ta phải làm như thế nào ?

Chúng ta sẽ define một Class , gọi là BenefitReport như bên dưới

 


class BenefitReport{
    $protected $claimnant;
    public function __construct($claimnant){
        $this->claimnant = $claimnant;
    }
    public function printReport(){
        return $this->claimnant->name . " " . $this->claimnant->getBenefit();
    }
}

Và khi muốn hiển thị số benefit của từng người, ta có thể viết như bên dưới

$claimnants->each->benefitReports->printReport()

Chúng ta phải khai báo thêm class BenefitReports chỉ để làm nhiệm vụ in reports. Nhưng, đó có hẳn là mặt bất cập ?

Không hẳn thế, nếu việc khai báo thêm hàm isRetired vào object Claimnant giúp ta chuyển được logic liên quan đến object vào trong đúng object cuả nó, thì việc tạo thêm class BenefitReports giúp chúng ta có một design tốt hơn, thay vì chúng ta tạo ra thêm một hàm trong class Claimnant chúng ta đã tách bạch được thành class BenefitReports , support tốt hơn về cấu trúc hướng đối tượng (chẳng hạn sau này chúng ta phải thêm vào một Report kiểu khác như report hình cột, hình tròn hoặc biên độ … )

Các hàm của Collection có hỗ trợ Higher Order Messaging

Nếu tìm vào trong class Collection của laravel, các bạn sẽ thấy các hàm bên dưới được hỗ trợ
<pre><code class="language-php">
/**
* The methods that can be proxied.
*
* @var array
*/
protected static $proxies = [
'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap',
'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum',
];
</code></pre>
Chúng ta có thể thấy, khi gọi các hàm trên như bên dưới, ta sẽ nhận được return là 1 class
HigherOrderCollectionProxy`



>> $arr = collect([["a" => true, "b" => false], ["a" => false, "b" => false]]) => Illuminate\Support\Collection {#759 all: [ [ "a" => true, "b" => false, ], [ "a" => false, "b" => false, ], ], } >> $arr->filter => Illuminate\Support\HigherOrderCollectionProxy {#732}

Khi chúng ta gọi như bên dưới, thì kết quả là gì 😀


>> $arr->filter->a

Chúng ta thử nhìn vào đoạn code bên dưới trong class HigherOrderProxy


    /**
     * Proxy accessing an attribute onto the collection items.
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        return $this->collection->{$this->method}(function ($value) use ($key) {
            return is_array($value) ? $value[$key] : $value->{$key};
        });
    }

Ta thấy rằng, phần tử của collection không hẳn là objects, có thể là array cũng được :).
Quay lại với ví dụ trên, ta đoán được đoạn filter trên sẽ trả về mảng chứa mảng có key a = true như bên dưới


>>> $arr->filter->a
=> Illuminate\Support\Collection {#755
     all: [
       [
         "a" => true,
         "b" => false,
       ],
     ],
   }

Vậy ta có thể gọi theo kiểu object notation hoặc key 🙂

Bài đến đây đã dài, cũng xin kết thúc phần giới thiệu về High Ordering Messages.

Nếu các bạn vẫn còn hứng thứ với phong cách lập trình như thế này các bạn có thể đọc thêm

  1. Tacit Programming (lập trình tri thức ngầm):
  2. Currying functions (khái niệm này của javascript nhưng cũng khá hay trong functional programming)

Source tham khảo 

https://mattstauffer.com/blog/laravel-collections-higher-order-messaging-and-when-method-in-laravel-5-4/

http://natpryce.com/articles/000535.html

https://en.wikipedia.org/wiki/Higher_order_message

Leave a Reply

Your email address will not be published. Required fields are marked *