What can go wrong if the Liskov substitution principle is violated?

I was following this highly voted question on possible violation of Liskov Substitution principle. I know what the Liskov Substitution principle is, but what is still not clear in my mind is what might go wrong if I as a developer do not think about the principle while writing object-oriented code.

9

I think it’s stated very well in that question which is one of the reasons that was voted so highly.

Now when calling Close() on a Task, there is a chance the call will
fail if it is a ProjectTask with the started status, when it wouldn’t
if it was a base Task.

Imagine if you will:

public void ProcessTaskAndClose(Task taskToProcess)
{
    taskToProcess.Execute();
    taskToProcess.DateProcessed = DateTime.Now;
    taskToProcess.Close();
}

In this method, occasionally the .Close() call will blow up, so now based on the concrete implementation of a derived type you have to change the way this method behaves from how this method would be written if Task had no subtypes that could be handed to this method.

Due to liskov substitution violations, the code that uses your type will have to have explicit knowledge of the internal workings of derived types to treat them differently. This tightly couples code and just generally makes the implementation harder to use consistently.

12

If you don’t fulfill the contract that has been defined in the base class, things can silently fail when you get results that are off.

LSP in wikipedia states

  • Preconditions cannot be strengthened in a subtype.
  • Postconditions cannot be weakened in a subtype.
  • Invariants of the supertype must be preserved in a subtype.

Should any of these not hold, the caller might get a result he does not expect.

2

Consider a classic case from the annals of interview questions: you have derived Circle from Ellipse. Why? Because a circle IS-AN ellipse, of course!

Except… ellipse has two functions:

Ellipse.set_alpha_radius(d)
Ellipse.set_beta_radius(d)

Clearly, these must be re-defined for Circle, because a Circle has a uniform radius. You have two possibilities:

  1. After calling set_alpha_radius or set_beta_radius, both are set to the same amount.
  2. After calling set_alpha_radius or set_beta_radius, the object is no longer a Circle.

Most OO languages don’t support the second, and for a good reason: it would be surprising to find that your Circle is no longer a Circle. So the first option is the best. But consider the following function:

some_function(Ellipse byref e)

Imagine that some_function calls e.set_alpha_radius. But because e was really a Circle, it surprisingly has its beta radius also set.

And herein lies the substitution principle: a subclass must be substitutable for a superclass. Otherwise surprising stuff happens.

7

In layman’s words:

Your code will have an awful lot of CASE/switch clauses all over.

Every one of those CASE/switch clauses will need new cases added from time to time, meaning the code base is not as scalable and maintainable as it should be.

LSP allows code to work more like hardware:

You don’t have to modify your iPod because you bought a new pair of external speakers, since both the old and the new external speakers respect the same interface, they are interchangeable without the iPod losing desired functionality .

6

to give a real life example with java’s UndoManager

it inherits from AbstractUndoableEdit whose contract specifies that it has 2 states (undone and redone) and can go between them with single calls to undo() and redo()

however UndoManager has more states and acts like an undo buffer (each call to undo undoes some but not all edits, weakening the postcondition)

this leads to the hypothetical situation where you add a UndoManager to a CompoundEdit before calling end() then calling undo on that CompoundEdit will lead it to call undo() on each edit once leaving your edits partially undone

I rolled my own UndoManager to avoid that (I probably should rename it to UndoBuffer though)

You can also look at it from a modelling point of view. When you say that an instance of class A is also an instance of class B you imply that “the observable behavior of an instance of class A can also be classified as observable behavior of an instance of class B” (This is only possible if class B is less specific than class A.)

So, violating the LSP means that there is some contradiction in your design: you are defining some categories for your objects and then you are not respecting them in your implementation, something must be wrong.

Like making a box with a tag: “This box contains only blue balls”, and then throwing a red ball into it. What is the use of such a tag if it shows the wrong information?

I inherited a codebase recently that has some major Liskov violators in it. In important classes. This has caused me huge amounts of pain. Let me explain why.

I have Class A, which derives from Class B. Class A and Class B share a bunch of properties that Class A overrides with its own implementation. Setting or getting a Class A property has a different effect to setting or getting the exact same property from Class B.

public Class A
{
    public virtual string Name
    {
        get; set;
    }
}

Class B : A
{
    public override string Name
    {
        get
        {
            return TranslateName(base.Name);
        }
        set
        {
            base.Name = value;
            FunctionWithSideEffects();
        }
    }
}

Putting aside the fact that this is an utterly terrible way to do translation in .NET, there are a number of other issues with this code.

In this case Name is used as an index and a flow control variable in a number of places. The above classes are littered throughout the codebase in both their raw and derived form. Violating the Liskov substitution principle in this case means that I need to know the context of every single call to each of the functions that take the base class.

The code uses objects of both Class A and Class B, so I cannot simply make Class A abstract to force people to use Class B.

There are some very useful utility functions that operate on Class A and other very useful utility functions that operate on Class B. Ideally I would like to be able to use any utility function that can operate on Class A on Class B. Many of the functions that take a Class B could easily take a Class A if it was not for the violation of the LSP.

The worst thing about this is that this particular case is really hard to refactor as the entire application hinges on these two classes, operates on both classes all the time and would break in a hundred ways if I change this (which I am going to do anyway).

What I will have to do to fix this is create a NameTranslated property, which will be the Class B version of the Name property and very, very carefully change every reference to the derived Name property to use my new NameTranslated property. However, getting even one of these references wrong the entire application could blow up.

Given that the codebase does not have unit tests around it, this is pretty close to being the most dangerous scenario that a developer can face. If I don’t change the violation I have to spend huge amounts of mental energy keeping track of what type of object is being operated on in each method and if I do fix the violation I could make the whole product explode at an inopportune time.

2

Example: You are working with a UI framework, and you create your own custom UI-control by subclassing the Control base class. The Control base class defines an method getSubControls() which should return a collection of nested controls (if any).
But you override the method to actually return a list of birthdates of presidents of the United States.

So what can go wrong with this? It is obvious that the rendering of the control will fail, since you don’t return a list of controls as expected. Most likely the UI will crash. You are breaking the contract which subclasses of Control is expected to adhere to.

If you want to feel the problem of violating LSP, think what happens if you have only .dll/.jar of base class (no source code) and you have to build new derived class. You can never complete this task.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật