Laravel Beauty: Tìm hiểu về Contract
Mở đầu
Tiếp nối series về các phần core của Laravel Framework, trong bài viết này, mình sẽ giới thiệu nốt về thành phần cuối cùng được nhắc tới trong mục Architecture Foundations của Laravel, bên cạnh những Service Container, Service Provider, Facade.
Đó chính là Contract.
Có lẽ đây cũng sẽ là bài viết cuối cùng trong series Laravel Beauty. Hy vọng thông qua series này, các bạn đã có được một cái nhìn tổng thể về kiến trúc của Framework Laravel, cũng như tự ngẫm được ra cho mình những phương pháp, những best practices để có thể sử dụng Laravel một cách thật hiệu quả. :)
Tìm hiểu về Contract
Contract là gì?
Contract dịch ra tiếng Việt thì có nghĩa là "hợp đồng". Các thành phần của Laravel làm việc và liên kết với nhau thông qua các "hợp đồng" đó.
Thực ra, "Contract" chỉ là thuật ngữ được Taylor Otwell, cha đẻ của Laravel, cũng như các đồng nghiệp của ông lựa chọn để là tên gọi cho một phần trong kiến trúc framework của mình từ phiên bản Laravel 5.0 mà thôi, chứ thực tế, bản chất của Contract là các ... Interface.
Thuật ngữ Contract này cũng không phải là mới mẻ gì, hay do Taylor nghĩ ra, mà nó vốn đã được sử dụng rất nhiều và phổ biến trong các ngôn ngữ lập trình hướng đối tượng, để chỉ Interface hay Abstract Class.
Việc sử dụng Interface sẽ giúp ta phát huy được tính đa hình của lập trình hước đối tượng, đó cũng chính là chìa khoá để có được một hệ thống thiết kế tốt, một well-designed-application.
Sử dụng Contract
Nếu đã từng xem qua và nắm vững về Service Container và Service Provider trong những bài viết trước của mình, chắc hẳn bạn vẫn còn nhớ rằng người ta vẫn thường dùng Service Container để
bind
một interface
với một implementation
của nó. Việc sử dụng interface như vậy giúp ta dễ dàng thay đổi implementation mà không làm ảnh hưởng gì đến tính đúng đắn của chương trình.
Đến đây hãy nhắc lại về Dependency Injection, khái niệm mình đã nhắc đến rất nhiều lần trong những bài viết thuộc series Laravel Beauty này, để thấy được Contract đang được sử dụng như thế nào trong Laravel nhé.
Đây là ví dụ được đưa ra ở phần cuối bài viết về Facade.
// Dependency Injection Style
class Something
{
protected $mailer;
public function __constructor(Illuminate\Contracts\Mail\Mailer $mailer)
{
$this->mailer = $mailer;
}
public function sendMail()
{
// Sending mail
$this->mail->send($view, $data);
}
}
Như các bạn đã biết thì Laravel, với sức mạnh của Service Container sẽ có thể tự động resolve ra một instance
$mailer
cho chúng ta. Nhưng vấn đề là chúng ta đang type-hint một Interface (Contract), chứ không phải là một class cụ thể. Thực tế logic của chúng ta cũng không cần biết và quan tâm đến cái class gửi mail nó là class gì, hay nó đã làm thế nào để có thể gửi mail. Cái duy nhất logic của ta quan tâm là nó cần một instance có thể gửi mail để hoạt động. Đó là instance của class nào không quan trọng, miễn là class đó có implement interface Illuminate\Contracts\Mail\Mailer
.
Hay nói cách khác, code của chúng ta không phụ thuộc vào một implementation (một concrete class) nào cụ thể cả, mà nó phụ thuộc vào một cái "hợp đồng"
Mailer
thôi.
Ngoài ra, nếu không muốn sử dụng Service Mailer có sẵn mà Laravel cung cấp, ta có thể tự viết ra một Service của riêng mình, đương nhiên là nó sẽ phải "thoả mãn" cái "hợp đồng" (phải implement cái interface)
Illuminate\Contracts\Mail\Mailer
rồi. Tiếp sau đó ta tiến hành bind cái contract Illuminate\Contracts\Mail\Mailer
với cái Service của mình vừa viết vào trong Service Container, và thế là xong. Những công việc còn lại Laravel đã lo hết cho chúng ta. =))Contract trong Laravel
Contract xuất hiện ở mọi ngõ ngách trong Laravel Framework.
Trên document của Laravel, Contracts còn được miêu tả là thành phần "serve as succinct documentation to the framework's features" (được coi như là bộ documentation ngắn gọn về các chức năng của Framework). Vì sao ư, bạn có thể hiểu đơn giản là nhìn vào Contract, ta có thể biết được Framework (hay trong nhiều trường hợp cụ thể hơn các Service của Framework) có thể "làm được những gì". Đó chính là thứ chúng ta quan tâm :D (bởi thực tế bạn cũng chẳng cần phải biết xem các Service đã "làm như thế nào" để thực hiện các chức năng của chúng đúng không =)), à trừ khi bạn có ý định muốn tìm hiểu kỹ để contribute cho framework thôi)
Về danh sách các Contract này, các bạn có thể tham khảo ở repo chính thức illuminate/contracts. Vào đấy bạn sẽ thấy lượng Contract của Laravle nó đồ sộ đến thế nào =))
Còn khi bạn xây dựng application của mình trên nền tảng Laravel, thì mặc dù thư mục
Contracts
không có sẵn, nhưng bạn có thể tự tạo ra một thư mục như thế, có thể là bên trong thư mục app
, hay bên trong các folder khác nữa, rồi đặt các Interface
của mình vào, cho nó giống với Laravel Style. :v
Đương nhiên bạn vẫn có thể tạo được ra một trang web mà không cần biết đến
Contract
là gì, hay cảm thấy quá bất tiện khi phải đi mò xem cái interface mình cần nó ở đâu nên không dùng đến. Bạn thấy mình vẫn có thể thực hiện Dependency Injection mặc dù không cần dùng đến Interface, mà thay vào đó là type-hint tên class cụ thể, đấy thôi. (đương nhiên thì trong phần lớn các trường hợp thì nó là một cách thức tồi rồi =)))
Hoặc là đôi khi bạn có thể dùng đến một "vũ khí" đầy sức mạnh khác của Laravel mà mình cũng đã đề cập ở phần trước, đó chính là Facade.
Bản thân
Các bạn có thể kiểm tra xem danh sách Contract, với Facade tương ứng với nó ở trang document của Laravel, từ đó bạn sẽ có thể biết được rằng "nếu mình không muốn dùng Facade này, thì mình phải dùng Dependency Injection với Contract nào để thay thế, và ngược lại".
Facade
cũng có quan hệ mật thiết với Contract
. Bởi Facade cũng chỉ là một công cụ che đi việc resolve ra service từ Service Container. Tức bạn thay đổi đối tượng binding của Contract với Service Container thì đối tượng phía bên dưới Facade (service mà Facade resolve ra) cũng sẽ thay đổi.Các bạn có thể kiểm tra xem danh sách Contract, với Facade tương ứng với nó ở trang document của Laravel, từ đó bạn sẽ có thể biết được rằng "nếu mình không muốn dùng Facade này, thì mình phải dùng Dependency Injection với Contract nào để thay thế, và ngược lại".
Lời kết
Như vậy là mình đã giới thiệu qua về Contract, cũng như vai trò của nó trong kiến trúc của Laravel Framework.
Thực tế thì Contract cũng khá là đơn giản, dễ hiểu, nên thật sự cũng không có nhiều điều để mà nói về nó cả. =))
Contract trong Laravel đơn giản chỉ là Interface.
Nếu bạn đã nắm rõ về Service Container và Service Provider thì có thể đã thấy được vai trò của Interface từ trước. Ngoài ra, một chút kiến thức cơ bản về các quy tắc thiết kế hướng đối tượng (Object Oriented Design Principles) có lẽ cũng sẽ giúp các bạn hiểu thêm về cách sử dụng cũng như tầm quan trọng của Interface đấy :D
Hy vọng có đã có thể giúp các bạn thêm hiểu, và thêm yêu quý framework PHP tuyệt vời này. (dance2)
Comments
Post a Comment