Nguyên lý SOLID được áp dụng trong PHP sẽ giúp chúng ta xây dựng ứng dụng, website một cách hiệu quả dễ dàng nâng cấp bảo trì về sau này. Tuy vậy làm sao để áp dụng được nguyên lý cùng với hướng đối tượng vào trong thực tế, thì chúng ta cùng xem ngay dưới đây với những vị dụ chi tiết cho từng nguyên tắc.
Nguyên lý SOLID ra đời giúp chúng ta xây dựng các ứng dụng hướng đối tượng một cách hiệu quả hơn. Tác giả của S.O.L.I.D chính là Robert C.Martin – tác giả nhiều cuốn sách nổi tiếng trong đó có cuốn Clean Code: A Handbook of Agile Software Craftsmanship
Trước đây từ khi đi học thông thường cách làm của các bạn sinh viên là làm sao cho chương trình chạy là tốt rồi, chứ chưa thực sự quan tâm nhiều tới việc mở rộng, hay tối ưu sau này. Và nguyên lý SOLID này cũng sẽ như là những kiến thức cơ bản để mọi lập trình viên có cơ hội review lại mình, từ đó tạo ra những mã nguồn chất lượng hơn.
Ngày hôm nay trong bài viết này sẽ giới thiệu cơ bản tới các bạn nguyên lý SOLID và cách áp dụng trong Hướng đối tượng PHP.
Nguyên Lý SOLID là bộ 5 nguyên tắc, mà mục tiêu của những nguyên tắc nhằm tạo ra những đoạn mã (code) ít làm ảnh hưởng đến phần còn lại. Điều này có nghĩa là nâng cấp, sửa chữa gây ảnh hưởng đến các phần còn lại càng ít càng tốt. Từ đó sẽ giảm bớt chi phí thời gian, tiền bạc, công sức bảo trì nâng cấp về sau này.
Cụ thể 5 nguyên tắc trong SOLID là
+ Nguyên tắc đơn chức năng – Single Responsibility Principle – SRP
+ Nguyên tắc đóng mở – Open/Closed Principle – OCP
+ Nguyên tắc thay thế – Liskov Substitution Principle – LSP
+ Nguyên tắc phân tách – Interface Segregation Principle – ISP
+ Nguyên tắc nghịch đảo phụ thuộc – Dependency Inversion Principle – DIP
Mỗi class chỉ nên giữ 1 trách nhiệm duy nhất. Hay nói cách khác Một class chỉ nên có một và chỉ một lý do để thay đổi mà thôi.
A class should have one and only one reason to change, meaning that a class should have only one job.
<?php namespace CodeTuTam\Solid\Example; class User{ //Lấy thông tin User từ Database public function getUserFromDatabase(){ // Code lấy thông tin user } // Hàm Login public function login(){ //Login với user hiện tại } //Hàm validate dữ liệu trước khi xử lý public function validate(){ } // Hàm ghi log xử lý public function log(){ // do something?? } }
Trên đây là ví dụ về Class User trong PHP
Trong class này chúng ta thấy 1 số hàm như
Hàm getUserFromDatabase: Lấy thông tin User từ database
Hàm login: Thực hiện đăng nhập
Hàm validate: Thực hiện validate dữ liệu
Hàm log: Ghi log chương trình
Nếu đối chiếu với nguyên tắc đơn chức năng trong nguyên lý SOLID thì class User trên đây đang vi phạm rõ ràng. Khi mà trong class User đang thực hiện quá nhiều nhiệm vụ trong 1 class
Để thực hiện theo nguyên tắc được đưa ra thì chúng ta có thể tách class bên trên thành nhiều class nhở, với mỗi class thực hiện 1 chức năng riêng biệt
<?php namespace CodeTuTam\Solid\Example; class User{ // Hàm Login public function login(){ //Login với user hiện tại } } class UserResposity{ //Lấy thông tin User từ Database public function getUserFromDatabase(){ // Code lấy thông tin user } } class UserValidator{ //Hàm validate dữ liệu trước khi xử lý public function validate(){ } } class UserLog{ // Hàm ghi log xử lý public function log(){ // do something?? } }
Đối tượng, class có thể mở rộng 1 cách thoải mái, nhưng không được phép sửa đổi bên trong class đó
Objects or entities should be open for extension, but closed for modification.
Mới đầu khi nghe qua điều trên thì chúng ta sẽ thấy thật vô lý, không hiểu kiểu gì mà mở rộng thoải mái nhưng lại không được phép sửa đổi. Nhưng việc này hoàn toàn xử lý được bằng các kĩ thuật khác, ví dụ như Kế Thừa các cần mở rộng. Như vậy chúng ta sẽ không phải sửa đổi class đã có mà chỉ cần thao tác mở rộng trên class mới tạo ra.
Việc tạo class kế thừa quá nhiều cũng cần chú ý để tránh hiện tượng tạo ra sự sai khác quá nhiều so với class gốc, có thể dẫn tới nghiệp vụ xử lý bị ảnh hưởng.
Để làm rõ hơn vấn đề này chúng ta xem ví dụ dưới đây
Chú ý các đoạn mã viết trên nền PHP 7
Ví dụ bây giờ chúng ta xây dựng 1 lớp User để thực hiện lưu dữ liệu vào database. Tuy vậy nhiệm vụ của chúng ta là phải kiểm tra tính đúng đắn của dữ liệu trước khi lưu vào database.
Yêu cầu kiểm tra rất đơn giản là chỉ cần tên user khác rỗng là được lưu vào database.
Và đoạn code dưới dây thể hiện điều này
<?php namespace CodeTuTam\Solid\OCP\Examples; class UserValidator{ public function validate($data):bool{ if($data['name']!=''){ return true; } return false; } } class User{ public function save($data=['name'=>'Tuấn','age'=>18]){ $userValidator = new UserValidator(); if($userValidator->validate($data)){ echo 'Dữ liệu thỏa mãn, lưu vào database'.PHP_EOL; //Lưu vào database chẳng hạn??? } else{ //Thông báo lỗi... echo "Lỗi rồi!".PHP_EOL; } } } $user = new User; $user->save();//Dữ liệu thỏa mãn, lưu vào database
Tuy vậy có 1 trường hợp phát sinh ra sau này, bây giờ không cần kiểm tra name khác rỗng nữa (vì 1 lý do nào đó ví dụ như đã kiểm tra ở bước trước rồi) mà thay vào đó phải kiểm tra độ tuổi lớn hơn 0 thì mới lưu database
Việc này dẫn đến chúng ta phải sửa class Validator ban đầu. Điều này thực sự tệ hại. Hãy tưởng tượng, vào 1 ngày kia yêu cầu mới đưa ra là quay về kiểm tra name nhập vào. Điều này dẫn tới việc chúng ta mất nhiều thời gian chỉnh sửa mà tạo ra nhiều tiềm ẩn rủi ro trong quá trình phát triển.
Để đảm bảo theo nguyên tắc đóng mở, chúng ta sửa lại đoạn mã trên như sau
<?php namespace CodeTuTam\Solid\OCP\Examples; interface IValidator{ public function validate($data):bool; } class NameValidator implements IValidator{ public function validate($data):bool{ if($data['name']!=''){ return true; } return false; } } class AgeValidator implements IValidator{ public function validate($data):bool{ if($data['age']>0){ return true; } return false; } } class User{ protected $validator; public function __construct(IValidator $validator){ $this->validator = $validator; } public function save($data=['name'=>'Tuấn','age'=>18]){ if($this->validator->validate($data)){ echo 'Dữ liệu thỏa mãn, lưu vào database'.PHP_EOL; //Lưu vào database chẳng hạn??? } else{ //Thông báo lỗi... echo "Lỗi rồi!".PHP_EOL; } } } $user = new User(new NameValidator); $user->save(); $user = new User(new AgeValidator); $user->save();
Ví dụ trên đây hi vọng giúp các bạn hiểu phần nào nguyên tắc này. Trường hợp trong thực tế có thể không simple như vậy và có thể xử lý cách hay hơn. Tuy vậy hãy nhớ tới Nguyên Tắc Đóng mở để đảm bảo cho mã nguồn sáng sủa.
Nguyên tắc đóng mở – Open-Closed Priciple có thể đạt được bằng nhiều cách khác nhau. Ví dụ như sử dụng thừa kế (inheritance) hoặc thông qua các mẫu thiết kế tổng hợp (compositional design patterns) như Strategy pattern.
Trên đây là 2 nguyên tắc cơ bản đầu tiên trong nguyên lý SOLID. Kiến thức dựa trên kinh nghiệm cá nhân và tìm hiểu của bản thân nên không tránh được những sai sót, các bạn đọc tham khảo, có gì góp ý để cùng nhau tiến bộ nhé 😀
Trong phần tiếp theo của bài viết mình sẽ giới thiệu tiếp 3 nguyên tắc còn lại của nguyên lý SOLID là Nguyên tắc thay thế – Liskov Substitution Principle – LSP, Nguyên tắc phân tách – Interface Segregation Principle – ISP và Nguyên tắc nghịch đảo phụ thuộc – Dependency Inversion Principle – DIP.
Nếu thấy bài viết có ích vui lòng like và share để ủng hộ bọn mình nhé.
Bình luận: