Professional Documents
Culture Documents
Có quá nhiều lợi ích để chúng ta tuân thủ theo S.O.L.I.D phải không nào?
Tất nhiên nguyên lý vẫn là nguyên lý. Bạn hoàn toàn có thể không thể tuân
thủ. Hứng đâu thì viết code ở đó. Nhưng nên nhớ là đây là trích rút ra từ
kinh nghiệm của rất nhiều thế hệ developer đi trước. Còn bây giờ chúng ta
cùng đào sâu tìm hiểu về những nguyên tắc này:
1. Single Responsibility
2. Open/Closed
3. Liskov Substitution
4. Interface Segregation
5. Dependency Inversion
S.O.L.I.D là thực chất là ghép của 5 chữ cái đầu của các nguyên lý. Đọc vậy
cho dễ nhớ. Mà thực chất khi bạn có thể áp dụng thành thục những
nguyên lý trên bạn cũng có thể trở thành Solid developer hay chính là "Lập
trình viên cứng". Bây giờ chúng ta cùng mổ sẻ từng nguyên lý một.
Coder.java
System.out.println("Đang code");
Coder.java
public class Coder {
System.out.println("Đang code");
TextUtils.java
Sau khi refactor xong thì code của chúng ta đã dễ hiểu, dành mạch, rõ ràng
hơn rất nhiều. Tất nhiên là cũng dễ maintain hơn nữa.
2. Open/closed principle
Đây là một trong những nguyên lý rất quan trọng trong quá trình phát
triển phần mềm. Nguyên lý mà chúng ta cần luôn luôn ghi nhớ và áp dụng
thường xuyên. Nội dung nguyên lý:
Chúng ta có thể thoải mái mở rộng class nhưng không được chỉnh sửa nội
dung bên trong nó.
Nguyên lý này nghĩa là: Mỗi khi chúng ta thiết kế một class nào đó thì
chúng ta cũng viết làm sao cho sau này, mỗi khi một developer muốn thay
đổi luồng trong ứng dụng. Họ chỉ cần thừa kế class ta đã viết hoặc override
một hàm nào đó.
Khi tôi muốn viết một class tính toán diên tích của các hình thay vì viết một
class cụ thể để thực hiện điều này tôi sẽ tạo một abstract class chung. Sau
đó cho các class là các hình cụ thể implement nó:
Shape.java
return Width*Height;
}
return Radius*Radius*Math.PI;
Sau này có một developer X nào đó muốn tính thêm diện tích hình nào đó
thì cũng làm tương tự như với class Rectangle và Circle. Cũng chỉ cần thừa
kế class Shape và override phương thức Area.
Giả sử chúng ta có 3 class; Vit ( base class ) và VitPin (class con thừa kế từ
class Vit), VitBau (class con thừa kế từ class Vit)
Vit.java
public abstract class Vit {
VitPin.java
@Override
if (Pin){
super.keu();
else {
try {
} catch (Exception e) {
e.printStackTrace();
Pin = pin;
VitBau.java
@Override
super.keu();
// Chạy phương thức bên dưới gây ra Exception vì lúc này chúng ta
chưa set Pin
// Pin = false
vitPin.keu();
Như bậy clas VitPin đã vi phạm nguyên lý của chúng ta vì VitPin là class con
không thể thay thế hoàn toàn class cha - Vit.
Bạn tưởng tượng chúng ta có một chiếc sạc điện thoại đa năng. Một đầu là
một ổ cắm điện. Đầu kia là 3, 4 chiếc dây để phù hợp với từng dòng này
khác nhau: Type - C, Micro Usb, Sạc chân to, sạc chân nhỏ.... Thoạt nhìn thì
chúng ta có vẻ rất tiện và gọn. Nhưng trong quá trình sử dụng thực tế thì
cực kì vướng víu. Tôi chỉ cần một đầu dây Type - C để sạc điện thoại của
mình. Trong khi đó lại có một mớ dây sạc loại điện thoại khác không cần
dùng đến. Việc thiết kế interface cũng như vậy. Nên tuân thủ nguyên lý
trên để tránh tình trạng "sạc điện thoại đa năng".
Ví dụ thực tế:
IVanDongVien.java
void nhayCao();
void nhayXa();
void boi();
}
VdvBoiLoi.java
@Override
// không implement
@Override
// không implement
@Override
System.out.println("Đang bơi");
@Override
System.out.println("Nhay cao");
@Override
System.out.println("Nhay xa");
@Override
// Không implement
IVdvBoi.java
void boi();
}
IVdvNhay.java
void nhayCao();
void nhayXa();
VdvBoiLoi.java
@Override
System.out.println("Đang bơi");
VdvNhay.java
@Override
@Override
System.out.println("Nhay xa");
Vậy là chúng ta tránh được tình trạng có phương thức mà không được
implement sau khi chia nhỏ interface.
Khi trong quá trình làm ứng dụng thực tế, khi áp dụng Dependency
Inverse, ta chỉ cần quan tâm tới interface. Để gửi một đoạn message chắng
hạn ta chỉ cần quan tâm đến hàm sendMessage() của interface
ISendMessage. Sau này khi cần thay đổi ta cũng cần thay đổi implement
của interface trên mà thôi. Ta có thể swap qua lại giữa: SendEmailMessage,
SendSmsMessage cùng implement interface ISendMessage.
Ví dụ:
ISendMessage.java
void sendMessage();
SendEmailMessage.java
this.message = message;
@Override
SendSmsMessage.java
this.message = message;
@Override
User.java
iSendMessage.sendMessage();
user.sendMessage(new SendEmailMessage("hello"));
user.sendMessage(new SendSmsMessage("hello"));
// in ra: hello được gửi qua Sms
Khi thay đổi yêu cầu thì ta cũng dễ dàng chuyển đổi mà không ảnh hưởng
tới code cũ. Như trên là ta có thể chuyển tùy ý giữa các class:
SendEmailMessage và SendSmsMessage. Hoặc thậm chí nếu phát sinh
thêm yêu cầu mới là gửi message qua Facebook. Thì ta cũng chỉ cần tạo
thêm class SendFacebookMessage, sau đó implement ISendMessage
interface.
Vừa rồi là trình bày của mình về S.O.L.I.D nguyên lý trong lập trình
hướng đối tượng. Nội dung cũng khá dài. Các bạn cố gắng áp dụng vào
code. Vì một tương lai Solid Developer. Chúc các bạn thành công.
Lập trình HDT là 1 pp lập trình mà chúng ta xem xét 1 ctr là tập hợp của các đối tượng
Các object ở đây có ID là duy nhất, thứ 2 là có trạng thái , hành vi, đặc điểm
Class giống như 1 template(khung sườn chung) để thể hiện các method,properties và behaviors của đối
tượng đó
+ Encapsulation
- Là 1 trong các tính chất lập trình hdt. Mục đích của nó bảo vệ tính nhất quán, toàn vẹn của dữ liệu ,che
dấu ẩn i nhựng thông tin .hạn chế ko cho phép truy cập thay đổi trực tiếp các properties, data. Mà chỉ
cho phép truy cập,xem,update thông qua các phương thức được public,geter/seter
-> Public là everywhere. Truy cập mọi nơi. Trực tiếp các properties
-> Default là truy cập trong nội bộ những lớp có cùng package
-> Protected tương tự default truy cập tại lớp khai báo và lớp con được kế thừa
+Inheritance
- là 1 trong các tính chất của lập trình hdt. Cho phép tạo 1 lớp con kế thừa từ lớp cha. Class con có quyền
dc use các method, properties của class cha mà ko phải private. Class cha có những cái j mà ko private
thì thằng con use dc.
+Polymorphism
- overload cùng tên nhưng khác thứ tụ tham số truyền vào. Khi compiler se bt chính xác thằng nào dc gọi
+Abstraction
- nó chỉ khao báo phần chung. ẩn phần chi tiết. Các class con sẽ triển khai
1) Trựu tường là những thứ trựu tượng chưa rõ ràng
- Tính trừu tượng là cách thể hiện mà chỉ cần khai báo phần chung là phần cần thiết
+ ViDu: Lớp staff có getWorkingTime() là phần chung. Nhưng chúng ta ko biết lấy thời gian của ai
+ Nên các lớp con kế thừa từ lớp staff phải implement lại phương thức getWorkingTime của lớp
Staff
+ interface
+ Abstract class
1) Interface chỉ cung cấp bộ khung xương
- chỉ dc khai báo những pthuc cần thiết
- Interface chỉ khao báo các phần tên ko có phần body
- Nếu ko khai báo acess modifer cho các pthuc trong interface thì mặc định là public
-
- Lớp con kế thừa từ abstract class buộc phải implement các method của abstract class
- Abstract lưu lại những thông tin chung
- nếu 1 lớp abstract khai báo những phương thức có phần thân ko có từ khóa abstract thì lớp con kế
thừa có ko bắt buộc phải implement các method này
-> Lý do:
+ vì abstract class khi khai báo. Lớp con kế thừa và bt chính xác implement chỗ nào
+ Còn interface thì rất nhiều thằng implement . có những class nó implement khác nhau. Class a
im class b. class b im class a. Nên khi chạy nó phải kiềm thằng nào muốn implement nên nó tốn thời
gian
+ Cùng 1 hành động tính tổng nhưng có nhiều cách tính khác nhau
Tính đa hình là 1 trong những tính chất cơ bản của lập trình hướng doituong
+ Tính dh cho phép các đối tượng cùng kiểu (cùng kiểu lớp cha) thực hiện những hành vi khác nhau dựa
vào từng đối tượng
+ Tính dh thể hiện qua overload trong khoản thời complier time. Nó sẽ bt chính xác thằng nào dc gọi.Khi
Đưa vào thứ tự tham số truyền vào
+ Tính dh thể hiện qua overiding. nó thể hiện thông qua lúc run time