2

For example, I have a Label class where the default font color is black:

public class MyLabel{
    protected int r=0;
    protected int g=0;
    protected int b=0;

    public void setRGB(int r,int g,int b){
        this.r=r;
        this.g=g;
        this.b=b;
    }
}

and a subclass where the default color changes to white:

public class MySpecialLabel extends MyLabel{
    public MySpecialLabel(){
        this.r=255;
        this.g=255;
        this.b=255;
    }
}

Does this violate the Liskov Substitution Principle? On one hand, the base class doesn't limit the value of rgb and rgb can change during the lifetime of the object, hence the preconditions and invariant rules don't apply. The Liskov Substitution Principle doesn't limit the constructor, so it seems this doesn't violate the Liskov Substitution Principle. On other hand, if some clients rely on the default value of MyLabel,eg:

this.setBgColor(Color.WHITE);
MyLabel label=new MyLabel();
this.addChild(label);

then replacing new MyLabel() with new MySpecialLabel() would make the label invisible, because they have the same color, requiring to add an extra line label.setColor(0,0,0) to fix it:

this.setBgColor(Color.WHITE);
MyLabel label=new MySpecialLabel();
label.setColor(0,0,0);
this.addChild(label);

It seems MyLabel cannot be replaced by MySpecialLabel without any adaption. So I don't know whether it is violating Liskov Substitution Principle, or not.

9
  • 6
    Commonly, "is this an LSP violation?" questions ignore the fact that LSP focuses on adhering to the promised contract, which means the question is unanswerable if you don't set out what the agreement was in the first place. I see nothing in the MyLabel contract that promises a specific color value, so it can't possibly violate something that was never promised in the first place, can it?
    – Flater
    Commented May 16 at 4:38
  • @Flater the compiler and assertions establish contract if otherwise unspecified
    – Basilevs
    Commented May 16 at 4:42
  • 2
    @Basilevs: The contract is considerably more than what a compiler can verify. There are tons of examples of LSP violations online that do not present as a compiler error nor something that a compiler would ever catch.
    – Flater
    Commented May 16 at 5:07
  • 2
    Agreed, but if a contract is unspecified there is no contract. Which means everything that could be possible is allowed.
    – Basilevs
    Commented May 16 at 5:08
  • BTW Are you seriously storing a Color as three separate ints? OMG.
    – gnasher729
    Commented May 16 at 6:57

4 Answers 4

4

The Liskov substitution principle says:

If f is a formula such that f(x) is true for all objects x of type T, then f(y) should be true for all objects y of type S, which is a subtype of T.

So first of all, this refers to objects that already exist. So what is the importance of constructor? It only tells us which objects are valid and which are not, meaning it restricts the space of objects of a given type. And that is important, as we will see soon.

Now your example of course satisfies LSP. Simply because those fields are protected, meaning not visible from the outside. And therefore their values are irrelevant, we cannot test them. In other words: what is f allowed to do? It is only allowed to call setRGB which has no observable effect.

Therefore I will assume you meant to make them public. And since we have setRGB method, I will assume those fields are publicly readonly (i.e. modifiable only internally by the class itself).

In this situation, is there a formula f such that f(x) is true for all objects of type MyLabel, but there is some y of type MySpecialLabel that doesn't satisfy f(y)? No. Assume that f(y) is false. Then I can create x, and set its values through setRGB method to the same values as y. Then x behaves exactly as y, and so f(x) has to be false as well. So even though technically y is of different type, in reality it is just a special case of x.

The situation is different though, if we remove setRGB method. Or for simplicity consider this:

public class MyLabel {
    public readonly int r;

    public MyLabel() {
        this.r = 0;
    }
}

public class MySpecialLabel extends MyLabel {
    public MySpecialLabel() {
        this.r = 255;
    }
}

This now breaks LSP. Indeed, the simple test obj.r == 0 is satisfied by all instances of concrete MyLabel type (remember that r is not modifiable after construction) but no instance of MySpecialLabel satisfies it.

On other hand, if some clients rely on the default value of MyLabel

The client cannot write a test that accepts MyLabel instance and verifies whether those values are default or not. The client would have to create that instance. But in such scenario the client knows its concrete type. And so it knows all about that instance, there's no replacement of any form, and LSP doesn't really apply here.

Such concern would be valid if we were to test MyLabel factory, and a subfactory. But that's not the case.

0
2

The principle is that I can substitute an x for an y. If I have three classes for red, green and blue buttons, the behaviour is “shows in the correct Color”, which they all three do. Not “shows in red” and the other two are in some violation.

As a rule of thumb: If you think that Liskov forces you to do something stupid, then 99% of the time you have misunderstood it.

PS If someone relies on the default Color of the base class instead of checking the default Color of the derived class, that’s a violation of the “don’t hire numpties” principle.

1
  • Consider adding a notion about setter like in your comments
    – Basilevs
    Commented May 18 at 9:36
0

LSP is about the contract offered by a type. If the black background is not part of the contract guaranteed by the base class, and if the class invariants do not limit the range of possible colors, then your code doesn't infringe LSP (at least not for this reason).

Remark: The constructor is in principle not subject to LSP. However, this exclusion is only partial, since constructors must deliver objects that meet the class invariants guaranteed by the type. This is why the contract is decisive here.

1
  • 3
    And obviously the Color can’t be part of the contract if you have a method to change it.
    – gnasher729
    Commented May 16 at 18:46
-2

LSP does not apply to constructors because in most languages instantiation is impossible without explicit type specification. Therefore the only contract for MySpecialLabel() is not to break internal lifetime scoped invariants which is impossible anyway in OOP languages.

So you code violates LSP no more than any Java code using objects without creation patterns.

2
  • 2
    i did not downvote. However I think the argumentation with the constructor is only partially valid, as the constructors must in the end deliver objects that comply with the class invariants.
    – Christophe
    Commented May 16 at 6:46
  • 1
    It is 100% valid because you can’t substitute another class while the constructor is running. And there is no violation without substitution.
    – gnasher729
    Commented May 16 at 6:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.