Views: 434
Mục tiêu
Thông qua một demo nhỏ sử dụng Laravel Framework giúp hiểu rõ hơn về cách thiết kế kiến trúc hệ thống website dễ bảo trì, theo tiêu chuẩn “Coding to Interface”, hay nôm na là “Dependency Injection”.
Định nghĩa
Theo võ lâm giang hồ
Các module cấp cao không nên phụ thuộc vào module cấp thấp và ngược lại, tất cả chỉ nên phụ thuộc vào INTERFACE.
Thần chú cần nhớ
CLASS CONCRETE nào implement INTERFACE nào thì binding INTERFACE đó vào CLASS CONCRETE đó.
Mô tả demo
Chúng ta sẽ triển khai chức năng gửi thông báo cho user, trong việc thông báo này sẽ bao gồm hai thứ: gửi mail, gửi sms.
Phương pháp chính luôn hiệu quả trong lập trình là chia để trị, ở đây chúng ta không dồn tất cả các tính năng gửi mail, gửi sms, … vào chung một hàm, một class, vậy thì làm thế nào để tách bạch từng chức năng, nhưng vẫn đảm bảo yêu cầu chạy tất cả chức năng được yêu cầu cùng một lượt?
Triển khai
Đăng ký Service Provider
Tạo class NotifyServiceProvider
theo path sau: app/Providers/NotifyServiceProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
use Illuminate\Support\ServiceProvider; class NotifyServiceProvider extends ServiceProvider { /** * Register bindings in the container. * * @return void */ public function register() { // bind concrete class to interface here } } |
Tiếp theo là cho Laravel nhận biết được Provider bạn vừa tạo,
mở file config/app.php
và thêm một phần tử vào mảng providers
1 2 3 4 |
'providers' => [ // Add thêm dòng này vào App\Providers\NotifyServiceProvider::class, ] |
Tạo bản thiết kế
Liên tưởng việc build một ứng dụng cũng giống như với việc xây nhà, bước đầu tiên là cần có một bản thiết kế.
Trong lập trình, bản thiết kế được hiểu là interface, và bây giờ chúng ta sẽ bắt đầu khởi tạo interface cho demo này.
Tất cả các files interface, chúng ta sẽ đặt theo path app/Services/Contracts/
, lần lượt tạo các files như sau:
Mẫu thiết kế chung cho việc gửi thông báo
1 2 3 4 |
interface Notify { public function send($subject, $template, $data); } |
Mẫu thiết kế gửi Mail
1 2 3 4 |
interface MailAdapter { public function send(...); } |
Mẫu thiết kế gửi SMS
1 2 3 4 |
interface SmsAdapter { public function send(...); } |
Hiện thực bản thiết kế
Các files hiện thực interface chúng ta sẽ để theo path app/Services/
Implement các class concrete cho việc gửi thông báo (Notify)
Thêm mới class EmailOnly
để thực hiện việc gửi mail để thông báo (Notify)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
use App\Services\Contracts\Notify; use App\Services\Contracts\MailAdapter; class EmailOnly implements Notify { protected $mailer; public function __construct(MailAdapter $mailer) { $this->mailer = $mailer; } public function send($subject, $template, $data) { $this->mailer->send($subject, $template, $data); } } |
Thêm mới class SmsAddon
để thực hiện việc tích hợp gửi Sms vào tác vụ gửi thông báo (Notify)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
use App\Services\Contracts\Notify; use App\Services\Contracts\SmsAdapter; class SmsAddOn implements Notify { protected $sms; protected $notifier public function __construct(Notify $notifier,SmsAdapter $sms) { $this->notifier = $notifier; $this->sms = $sms; } public function send($subject, $template, $data) { $this->notifier->send($subject, $template, $data); $this->sms->send($subject, $template, $data); } } |
Implement các concrete class cho từng interface của mỗi chức năng tích hợp
Hiện thực LaravelMailer
cho interface MailAdapter
(cụ thể việc gửi Mail)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
use Mail; use App\Services\Contracts\MailAdapter; class LaravelMailer implements MailAdapter { public function send($subject, $template, $data) { Mail::send($template, ['user' => $data], function ($m) use ($data) { $m->to($data->email, $data->name)->subject($subject); }); } } |
Hiện thực Sms
cho interface SmsAdapter
(cụ thể việc gửi Sms)
1 2 3 4 5 6 7 8 9 10 |
use App\Services\Contracts\SmsAdapter; use SmsPackage; class Sms implements SmsAdapter { public function send($subject, $template, $data) { SmsPackage::send(...) // làm gì đó thì làm ở đây } } |
Binding bản thiết kế vào class hiện thực
Về mô hình Dependency Injection, sau khi chúng ta thực hiện việc Thiết kế và Triển khai thiết kế, vẫn còn một bước cuối cùng, đó là xin chính quyền địa phương “cấp giấy phép vận hành”, cụ thể là binding các interface vào các class hiện thực chính interface đó.
Framework Laravel đã hỗ trợ chúng ta việc này rất tốt thông qua hệ thống Service Provider, việc của chúng ta chỉ đơn giản là chỉ ra interface nào sẽ binding vào class nào, thế là xong.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
use Illuminate\Support\ServiceProvider; class NotifyServiceProvider extends ServiceProvider { /** * Register bindings in the container. * * @return void */ public function register() { $this->app->singleton('MailAdapter', function ($app) { return new LaravelMailer(); }); $this->app->singleton('SmsAdapter', function ($app) { return new Sms(); }); $this->app->singleton('Notify', function ($app) { // return new EmailOnly(); //Nếu muốn gửi email thôi thì uncomment dòng này và comment dòng dưới return new SmsAddOn(new EmailOnly()); // EmailAndSms }); } } |