Đã biết OOP thì phải nắm vững thêm nguyên tắc ‘SOLID’ này
Tuy nhiên, để code tốt hướng đối tượng, thì chỉ 4 tính chất ban đầu vẫn chưa đủ. Bạn cũng nên tuân thủ những nguyên tắc phổ biến đi kèm, điển hình có thể kể đến là nguyên tắc SOLID.
Dù là đang đi học hay đi làm, hẳn mọi người sẽ không quá xa lạ khi nghe đến 4 tính chất trong lập trình hướng đối tượng là: Abstraction (tính trừu tượng), Encapsulation (tính đóng gói), Inheritance (tính kế thừa) và Polymophirsm (tính đa hình). Tuy nhiên, để code tốt hướng đối tượng, thì chỉ 4 tính chất trên sẽ chưa đủ. Bạn cũng nên tuân thủ những nguyên tắc phổ biến đi kèm, điển hình có thể kể đến là nguyên tắc SOLID.
SOLID có 5 ký tự, tượng trưng cho 5 nguyên tắc khác nhau, đó là:
The Single Responsibility Principle
The Open-Closed Principle
The Liskov Substitution Principle
The Interface Segregation Principle
The Dependency Inversion Principle
Giờ chúng ta sẽ cũng nhau tìm hiểu qua từng nguyên tắc nhé.
The Single Responsibility Principle
The Single Responsibility Principle phát biểu rằng: Mỗi class chỉ nên thực hiện một chức năng duy nhất, do đó ta chỉ thay đổi class chỉ vì một lý do duy nhất.
Ta có thể hiểu thêm về phát biểu này là các methods liên quan với nhau thì nên đưa vào cùng một class để có thể dễ quản lý và ngăn chặn sự lệ thuộc lẫn nhau giữa các method. Như vậy thì sẽ khó có thể gặp bug hơn cũng như dễ xử lý và tìm bug hơn.
Lấy ví dụ ta có đoạn code sau:
class Student
{
private:
std::string mStudentID;
double mMathScore, mOOPScore;
public:
double CalcAvarageScore();
void ExportAvgScoreToSheet();
void PrintAvgScore();
};
Thoạt nhìn, ta thấy đoạn code trên có vẻ đúng và hợp lý. Nhưng nếu như ta cần xuất điểm trung bình vào loại file khác mà không phải là sheet thì sao? Ta có thể thêm các hàm để export vào json, html, vân vân và mây mây. Class Student sẽ có rất nhiều method không liên quan. Và lúc này class Student đang phải làm khá nhiều việc. Thay vào đó, ta có thể tạo một class khác mang tên
Export
và tại đó implement đoạn code cho void ExportAvgScoreToSheet()
The Open-Closed Principle
The Open-Closed Principle phát biểu rằng: Một class nên dễ dàng mở rộng và cần hạn chế chỉnh sửa nội dung của class đó.
Ví dụ bạn đã code xong một class, bạn thấy class đó hoạt động tốt và không có bug gì cả. Tuy nhiên, về sau bạn nhận thấy mình phải thêm một vài chức năng cho class đó. Vậy thì bạn nên làm gì? Nếu lúc này bạn chỉnh sửa và code trực tiếp vào class bạn đã test là không có bug, như vậy thì bạn vừa mới tạo thêm cơ hội cho bug xuất hiện vào class đó đúng không nào.
Thay vào đó, bạn có thể sử dụng một số đặc điểm của hướng đối tượng là kế thừa và class trừu tượng (interface) để có thể thêm method mới vào mà không làm thay đổi class cũ.
The Liskov Subsitution Principle
Liskov Subsitution Principle có nội dung: Các object của class con có thể thay thế object của class cha mà không làm thay đổi tính đúng đắn của chương trình.
Nguyên tắc này có thể hiểu như thế này, bạn có class cha là hình chữ nhật, và bạn có class con là hình vuông. Thì khi chương trình hoặc hàm của bạn truyền vào object của class hình chữ nhật và object của class hình vuông thì chương trình phải xuất ra cùng một kết quả như nhau. Mình sẽ ví dụ bằng đoạn code hình chữ nhật như sau:
class Rectangle
{
protected:
double mWidth, mHeight;
public:
virtual void SetWidth(double width)
{
mWidth = width;
}
virtual void SetHeight(double height)
{
mHeight = height;
}
virtual double Area()
{
return mWidth * mHeight;
}
};
Và sau đó, bạn tạo class hình vuông kế thừa từ hình chữ nhật, và vì hình vuông có hai cạnh bằng nhau nên bạn sửa lại hàm
SetHeight
và hàm SetWidth
một ít.class Square : public Rectangle
{
public:
Square(double width) : Rectangle(width, width){};
void SetWidth(double width) override
{
mWidth = width;
mHeight = width;
}
void SetHeight(double height) override
{
mHeight = height;
mWidth = height;
}
double Area() override
{
return mWidth * mHeight;
}
};
Thoạt nhìn thì có vẻ ổn, nhưng thật chất, đoạn code hình vuông đã vi phạm nguyên tắc Liskov Subtitue, vì function sau sẽ trả về hai kết quả khác con trỏ hình chữ nhật khi các bạn truyền con trỏ hình vuông vào:
void test(Rectangle *shape)
{
shape->SetHeight(5);
shape->SetWidth(10);
std::cout << shape->Area();
}
Tất nhiên tất cả chỉ là ví dụ thôi nhé, hihi.
The Interface Segregation Principle
The Iterface Segregation Principle phát biểu rằng: Khi một interface (class trừu tượng) có quá nhiều methods thì hãy tách interface đó ra thành những interface nhỏ hơn.
Khi áp dụng nguyên lý này, ta sẽ tránh được tình huống class có quá nhiều methods không cần đến cũng như dễ dàng quản lý code và implement các methods hơn.
Chúng ta sẽ ví dụ bằng đoạn code sau:
class ParkingLot
{
virtual void ParkCar() = 0; //Decrease empty spot
virtual void UnpackCar() = 0; //Increase empty spot
virtual double CalculateFee(Car car); //Calculate fee base on number of hours
};
Có thể thấy, đoạn code trên là class trừu tượng cho một class bãi giữ xe. Tuy nhiên, nếu bãi giữ xe đó miễn phí thì sao, vậy thì sẽ dư một method không dùng đến là
CalculateFee
. Để giải quyết vấn đề này thì ta có thể tách method đó sang một class trừu tượng khác.The Dependency Inversion Principle
The Dependency Inversion Principle phát biểu rằng mỗi class khi kế thừa thì chỉ nên kế thừa từ interface hoặc class trừu tượng thay vì một class đã được implement.
Điều này sẽ giảm bớt sự phụ thuộc của các class với nhau, giúp hạn chế bug và dễ implement hơn.
Tổng kết
Vậy là chúng ta vừa tìm hiểu xem SOLID là gì rồi. Do chèn code vào bài viết nên mình không chèn ảnh minh hoạ thêm vào, hy vọng các bạn thấy bài viết hay. Hãy cố gắng áp dụng nguyên tắc SOLID khi code để code của bạn được sạch đẹp và dễ theo dõi hơn nghen.
Nếu các bạn thấy mình có sai sót trong bài viết thì hãy comment để mình biết và sửa nhé.
Nguồn tham khảo: FreeCodeCamp
Bài viết này được mình copy từ web của bản thân qua, mọi người hãy vào bài post gốc đọc các bài viết khác để ủng hộ mình nhé:
Người trong muôn nghề
/nguoi-trong-muon-nghe
Bài viết nổi bật khác
- Hot nhất
- Mới nhất