Nguyên lý SOLID trong lập trình hướng đối tượng – và ví dụ sử dụng C# – p3

Tìm hiểu về “L”- LSP (Liskov substitution principle)

Nguyên lý được phát biểu như sau:

So LISKOV principle says the parent should easily replace the child object.
Trong một chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình

Tiếp theo chúng ta tạo một class mới Enquiry sẽ kế thừa từ class Customer trên.

Chúng ta sẽ override 2 hàm GetDiscount() và Add() của class Customer như sau

class Enquiry : Customer
{
public override double getDiscount(double TotalSales)
{
return base.getDiscount(TotalSales) - 5;
}

public override void Add()
{
throw new Exception("Not allowed"); // Chú ý ở đây nhé các bạn :)
}
}

Như vậy chúng ta có 3 class: “Gold” , “Silver” và “Enquiry” đều kế thừa từ class Customer với mô hình đơn giản như sau
solid - nguyen ly trong lap trinh huong doi tuong

Tiếp theo chúng ta sẽ tạo một “list collection” của class “Customer”. Sau đó thêm vào 1 đối tượng “Silver”, “Gold” vả “Enquiry” cho list này.

Bây giờ chúng ta có thể duyệt qua list “Customer” này và gọi phương thức “Add”.

List Customers = new List();
Customers.Add(new SilverCustomer());
Customers.Add(new goldCustomer());
Customers.Add(new Enquiry());

foreach (Customer o in Customers)
{
o.Add();
}
}

Bây giờ thì chạy thử chương trình nào – Just do it right now!!! (đùa chút thôi, hãy dừng lại ở đây và suy nghĩ 1 chút, liệu có vấn đề gì với đoạn code trên không nhỉ? – Now again let me tickle your brains)…

Tất nhiên, sau khi dừng lại và phân tích, hoặc…chạy luôn đoạn code trên, kết quả vẫn là 1 exception 🙂

6

Bây giờ bạn đã hiểu ra vấn đề ở đây chưa? Nếu vẫn chưa – vui lòng độc lại lần thứ 2 (LOL đùa thôi)

Cùng xem lại vấn đề ở đây “Enquiry” có phương thức getDiscount(), Add() giống như “Customer” nhưng NÓ KHÔNG PHẢI LÀ MỘT CUSTOMER.

TẠI SAO? – thực tế ra hàm Add() của Customer thực hiện thao tác với Database nhưng hàm Add() của Enquiry thì KHÔNG!! Vậy đó Enquiry không giống như “Silver” hay “Gold”.

Nhớ lại phát biểu của nguyên lý:

So LISKOV principle says the parent should easily replace the child object.
Trong một chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình

Như vậy ở đây đã vi phạm nguyên lý LISKOV, khi mà class Enquiry với method Add() có hành vi (behavior) khác với class Customer.

Để implement LISKOV chung ta tạo 2 interfaces, 1 cho discount và 1 cho việc xử lý với database như sau:

interface IDiscount
{
double getDiscount(double TotalSales);
}

interface IDatabase
{
void Add();
}

Bây giờ class “Enquiry” sẽ chỉ implement “IDiscount” không bao gồm method “Add”.

class Enquiry : IDiscount
{
public double getDiscount(double TotalSales)
{
return TotalSales - 5;
}
}

Trong khi đó class “Customer” sẽ implement cả “IDiscount” và “IDatabase”.

class Enquiry : IDiscount
class Customer : IDiscount, IDatabase
{
private MyException obj = new MyException();
public virtual void Add()
{
try
{
// Database code goes here
}
catch (Exception ex)
{
obj.Handle(ex.Message.ToString());
}
}

public virtual double getDiscount(double TotalSales)
{
return TotalSales;
}
}

Giờ thì đơn giản hơn rồi. Khi viết lại đoạn code để gọi hàm Add() của các phần tử trong list Customer ta sẽ có ngay thông báo lỗi…

8

Một ví dụ khác

Hãy tưởng tượng bạn có 1 class cha tên Vịt. Các class VịtBầu, VịtXiêm có thể kế thừa class này, chương trình chạy bình thường. Tuy nhiên nếu ta viết class VịtChạyPin, cần pin mới chạy được. Khi class này kế thừa class Vịt, vì không có pin không chạy được, sẽ gây lỗi. Đó là 1 trường hợp vi phạm nguyên lý này.

Lược dịch từ: codeproject và internet

Written by thaotrinh