Lớp B chỉ nên thừa kế từ lớp A khi mọi hàm F thao tác trên các đối tƣợng của lớp A, cách cƣ xử của F không thay đổi khi ta thay thế các đối tƣợng của lớp A bằng các đối tƣợng của lớp B.
Thừa kế là một tính chất cơ bản và đặc trƣng của phƣơng pháp thiết kế, lập trình hƣớng đối tƣợng. Đó là việc xác định một lớp dựa trên lớp đối tƣợng đã có để sử dụng các thuộc tính, chức năng của đối tƣợng đã có đó, cũng nhƣ mở rộng thêm các chức năng của đối tƣợng này. Khi đó các đối tƣợng của lớp thừa kế sẽ có những hành vi tƣơng tự nhƣ các đối tƣợng của lớp cơ sở. Do đó, các đối tƣợng của lớp thừa kế có thể thay thế các đối tƣợng của lớp cơ sở để thực hiện các thao tác trên các đối tƣợng của lớp cơ sở.
Vì lý do trên mà ta không nên sử dụng tính chất thừa kế một cách tùy ý. Giả sử ta có lớp A và phƣơng thức F. Khi nâng cấp, mở rộng phần mềm, ta thêm vào một lớp mới, lớp B thừa kế lớp A. Tuy nhiên, việc thay thế đối tƣợng lớp A bằng đối tƣợng lớp B sẽ làm cho phƣơng thức F thực hiện những ứng xử sai khác so với trƣớc khi thực hiện thay thế. Do đó, ta phải chỉnh sửa lại hàm F để đảm bảo cách ứng xử không thay đổi. Điều này dẫn đến việc vi phạm nguyên lý đóng mở.
Ví dụ: ta hãy xem xét ví dụ sau để thấy đƣợc những hậu quả của việc thừa kế tùy tiện:
public class Stack{
private ArrayList data;
public virtual void push(int n){
// Đưa số nguyên n vào đỉnh ngăn xếp. }
public virtual int pop(){
// Lấy giá trị ở đỉnh ngăn xếp. }
}
public class Queue: Stack{
public override void push(int n){
// Thêm số nguyên n vào hàng đợi. }
public override int pop(){
// Lấy số nguyên từ hàng đợi. }
}
public int function1(Stack p){ p.push(2); p.push(3); p.push(4); int a = p.pop(); int b = p.pop(); if (a == 4 && b == 3)
55 return a * b;
throw new ArgumentException(); }
Để có thể sử dụng lại một số thuộc tính và phƣơng thức trong lớp Stack, ta cho lớp Queue thừa kế lớp Stack. Khi đó ta có thể truyền đối tƣợng Queue vào hàm function1. Tuy nhiên, cách cƣ xử của hàm này trên các đối tƣợng của lớp Stack và lớp Queue là khác nhau. Với đối tƣợng của lớp Stack, hàm function1 sẽ trả về tích của 2 số 4 và 3. Nếu đối tƣợng truyền vào là đối tƣợng của lớp Queue, hàm sẽ gây ra một ngoại lệ. Do đó ta phải chỉnh sửa lại hàm function1 để đảm bảo tính cƣ xử nhất quán. Điều này dẫn đến việc vi phạm nguyên lý đóng mở. Khi đó ta nói, hàm function1 vi phạm nguyên lý thay thế Liskov.
Một số chú ý khi xác định nguyên lý thay thế Liskov:
Nguyên lý thay thế Liskov có mối liên hệ chặt chẽ với nguyên lý đóng mở. Khi một thực thể phần mềm vi phạm nguyên lý thay thế Liskov, nó sẽ có những hành vi khác nhau trên các lớp cơ sở và lớp dẫn xuất. Để nó có thể có những thao tác đúng trên cả lớp cơ sở và lớp dẫn xuất, ta phải chỉnh sửa lại nó. Điều này dẫn đến việc vi phạm nguyên lý đóng mở.
Việc tuân thủ nguyên lý thay thế Liskov có tính chất tƣơng đối. Trong tình huống này, thực thể xem xét thỏa mãn nguyên lý, nhƣng trong tình huống cụ thể khác, nó lại không còn tuân thủ nguyên lý nữa. Tuy nhiên, khi thực hiện phân tích thiết kế hƣớng đối tƣợng, ta phải làm cho số lƣợng thực thể mà tuân thủ theo nguyên lý là nhiều nhất trong các tình huống thƣờng xảy ra, đặc biệt cho các thực thể hay đƣợc nâng cấp, mở rộng.