Kiến trúc hệ thống trên Laravel – phần 5

 

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 :).

Đây sẽ là 1 series gồm 1 số phần như sau:

  • 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
  • Phần 2 : OOP, Interface, Dependency Injection, IoC
  • Phần 3: Phân tích sâu vào việc sử dụng interface
  • Phần 4 : Design Pattern – Decorator
  • Phần 5 : Design Pattern – Adapter
  • Phần 6 : Design Pattern – Repository
  • Phần 7 : Design Pattern – Factory
  • Phần 8 : Advance component trong Laravel
  • Phần 9 : Mô hình kiến trúc cụ thể Part 1
  • Phần 10 : Mô hình kiến trúc cụ thể Part 

Hôm nay chúng ta sẽ đi vào phần adapter design pattern. Cái này cũng giống nguyên lý adapter ngoài đời thật thôi.

Kiểu như bạn có 1 cái sạc laptop 3 chân, nhưng ổ cắm chỉ nhận 2 chân -> bạn cần 1 cái adapter chuyển từ 3 chân (kết nối với sạc laptop) thành 2 chân (kết nối với ổ cắm)

Ví dụ khác, mac book pro chỉ hỗ trợ cổng xuất ra màn hình thông qua hdmi và thunderbolt. Nhưng bạn mua 1 cái màn hình chỉ hỗ trợ đầu vào là VGA hoặc DVI ==> bạn cần adapter

Vậy tác dụng lớn nhất của adapter là bạn hoàn toàn có thể kết nối các thành phần với nhau mà không làm thay đổi trạng thái ban đầu của chủ thể

Trong khi code cũng vậy, bạn luôn luôn phải tìm cách cố định các đoạn code đã ổn định (đã được test) nhưng phải sử dụng kết quả của đoạn code đấy để ghép vào các yêu cầu mới -> bạn mà edit đoạn code cõ sẵn để phù hợp với yêu cầu mới là vứt đi rồi.

OK, vào ví dụ nhé.

Giả sử mình có 1 hệ thống post comment lên facebook (sử dụng facebook php sdk cắm vào hệ thống của mình). Theo đúng bài, mình cần viết 1 interface để tích hợp package ngoài vào để post lên facebook.

 

interface SocialPosting
{
    public function post();
}

 

class FacebookClient sẽ thực thi interface này và tích hợp facebook SDK bên trong

 

class FacebookClient implements SocialPosting
{
    public function post() 
    {
        // Muốn làm gì với facebook thì làm ^^
    }
}

 

Binding thôi

 

$this->app->singleton('SocialPosting', function ($app) {
    return new FacebookClient();
});

 

Hệ thống đã hoàn thiện, interface SocialPosting đã được inject everywhere và chạy ngon lành cành đào hết rồi.

Một ngày đẹp giời, sếp lại muốn post lên Twitter. Ôi may quá, trước đó đã làm 1 hệ thống update status lên Twitter rồi. Đúng bài, hệ thống này cũng có 1 interface

 

interface SocialUpdate
{
    public function tweet();
}

 

Và có 1 class TwitterClient thực thi interface

 

class TwitterClient implements SocialUpdate
{
    public function tweet()
    {
        // Muốn làm gì với Twitter thì làm ở đây ^^
    }
}

 

Ok, vậy thì ta chỉ bê nguyên code của phần này migrate vào hệ thống cũ của ta là xong. Đơn giản quá. Nhưng mà hiện tại interface SocialPosting đang được sử dụng everywhere -> muốn sử dụng được Twitter thì chúng ta phải làm cách nào biến function tweet thành function post để có thể binding trong SocialPosting.

Có 2 hướng giải quyết ở đây

Cách 1: edit class TwitterClient, biến function tweet thành post rồi implement interface SocialPosting đúng như yêu cầu

 

class TwitterClient implements SocialPosting
{
    public function post()
    {
        // Muốn làm gì với Twitter thì làm ở đây ^^
    }
}

 

–> Đây là cách đơn giản nhất, và thực tế nếu nó không sinh ra error thì quá tuyệt vời, bạn có thể sử dụng được luôn -> nhưng mà thực tế code sẽ phức tạp hơn rất nhiều, và các method trong class sẽ phụ thuộc vào lẫn nhau nên việc thay đổi code có sẵn sẽ tiềm ẩn rất cao nguy cơ sinh ra lỗi :).

Vậy làm cách nào vừa giữ nguyên code cũ, vừa ghép vào hệ thống được?

Cách 2: sử dụng adapter

Viết 1 adapter để liên kết 2 phần với nhau

 

class TwitterAdapter implements SocialPosting
{
    protected $client;

    public function __construct(SocialUpdate $client)
    {
        $this->client = $client;
    }

    public function post()
    {
        $this->client->tweet();
    }
}

 

Các bạn thấy đấy, mình inject SocialUpdate vào trong class TwitterAdapter và lồng hàm tweet() bên trong method post() -> code cũ hoàn toàn ko bị thay đổi gì mà mình vẫn kết nối được các chức năng với nhau :).

Rồi, để sử dụng, chúng ta chỉ thay đổi phần binding là xong

 

$this->app->singleton('SocialPosting', function ($app) {
    return new TwitterAdapter(new TwitterClient);
});

 

Các bạn thấy đấy, Adapter rất hữu dụng khi bạn kết nối các [phần chức năng / các package] đã hoàn chỉnh với nhau mà không muốn phải thay đổi source code có sẵn của từng phần.

Cám ơn các bạn đã theo dõi, xin hẹn gặp lại các bạn vào bài tiếp theo.

 

Nguồn: http://blog.portalbeanzvn.com/kien-truc-he-thong-tren-laravel-phan-5/