1

Wiki says:

Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of T (correctness, task performed, etc.).

My understanding from this,

If T is an abstraction that provides encapsulation to protect invariants of state maintained by instances of T, then S is an abstraction that MUST at-least provide encapsulation to protect same invariants of those inherited states maintained by instances of S.

Here abstraction can not not only be a class but also a function. For example: function written in prototypical paradigm(ES5 JavaScript)

A class(T or S) knows, the contracts their instances should obey.

LSP is about S ensuring encapsulation to protect invariant of state, that gets inherited from T. My understanding is, correctness is about protecting invariants to maintain correctness of state.

Is that the right understanding?

9
  • 6
    It isn't about encapsulation; it's about functional correctness. For example, the .NET framework provides the IList interface, which has contracts such as the Add method. The framework also contains types that implement that interface, but throw the NotImplementedException when Add is called. As such, those types can be said to break the LSP.
    – David Arno
    Commented Oct 18, 2017 at 14:59
  • 1
    @DavidArno Functional correctness comes by protecting invariants on the state that a class abstraction maintains. I mentioned, what contract ensures, in the query. Contracts has nothing to do with functional correctness. One example code that I wrote that talks about Invariants of a class abstraction Commented Oct 18, 2017 at 19:30
  • @DavidArno Interfaces and specifications are two separate things. An interface in .net is not a contract at all. If it comes with some documentation, there may be an informal contract. A formal contract requires some kind of contract language. Commented Oct 19, 2017 at 1:22
  • Without the encapsulation of the base class state mutations, type substitution, preserving contracts, is not even possible. Commented Oct 19, 2017 at 1:23
  • @DavidArno Liskov's work is all about encapsulation. Commented Oct 19, 2017 at 17:17

3 Answers 3

10

No, I don't think so.

LSP says that S can substitute for T. What that means in practice is that S fulfills the same contractual obligations that T does. The writer of S can expose internal state (thereby breaking encapsulation) without violating the original API contract.

State is an implementation detail, not a behavior.

11
  • How an instance fulfill the contractual obligation? We express this by writing a class. So, class definition will ensure contractual obligation. Commented Oct 18, 2017 at 15:09
  • 2
    An instance fulfills the contractual obligation by providing behaviors that fulfill the contract. It may do this with or without the help of internal state. The encapsulation (or lack thereof) of said state has no effect on the contractual obligations. Commented Oct 18, 2017 at 15:15
  • An instance of type T, will fulfill contractual obligation of exposing behavior walk(), sleep() and state empty, because type T has expressed that contract, as class definition. If T has no state(empty) to mention, then that's fine. Contractual obligation can fulfill only when T expresses that contract. So, a class knows, what contracts should their instances should obey. Contracts are different from protecting invariants Commented Oct 18, 2017 at 15:45
  • True: contracts are different from protecting invariants. LSP has nothing to say about protecting the internal state of an object. LSP is only concerned about the contracts. Commented Oct 18, 2017 at 16:39
  • Then, what does correctness mean, in my query? Commented Oct 18, 2017 at 17:03
3

Not completely. If some base class provides an immutability guarantee for its state, then its subtypes should also provide that guarantee. If some base class guarantees some invariant relation for its state, then its subtypes should also make that guarantee. But there's also a lot more "desirable properties" of classes (and functions) than how their state behaves.

Another good way to look at it is described in Liskov's original paper, which this question summarizes: subtypes should not strengthen preconditions or weaken postconditions.

2
  • 2
    In order for that immutability guarantee to be relevant to SRP, it would have to be embodied in the API's behavior. So the contract is still king. Commented Oct 18, 2017 at 16:47
  • With complete encapsulation of the base class, it is impossible to break the base class state invariants. Commented Oct 19, 2017 at 1:18
-4

Yes, it is all about preserving invariants, as well as preconditions and postconditions. However, Liskov substitution is not a principle, simply something that you must do in order to perform software engineering using contracts.

When encapsulation is complete, a derived class cannot change the state invariants of the base class, even if the author desires to perform such a change.

The word "principle" was added about the same time widespread misunderstandings of Liskov's work began to emerge. If you read her original book with Guttag, Abstraction and Specification in Program Development (using CLU), or her later works about type substitution, you will find it is all about formal and informal contract specifications. For some reason, Liskov's ideas were eventually interpreted as being about the implementation of method signatures in languages such as Java that support interfaces. Of course such languages have no contracts so you can have many interpretations about what the contract should be.

3
  • I think, I mentioned about contract here, on page 3 Commented Oct 19, 2017 at 3:10
  • Be careful to distinguish between general ideas regarding formal verification (i.e. Liskov) and language specific ideas, such as the "interface" in Java. Java has no contract language. Contracts (specifications) can be formally specified in some languages. In such a language, if there is an "empty class" idea such as the Java interface, you might be able to put a specification on such an empty class, with the expectation that the compiler enforces this at compile time on classes inheriting from that (implementing the interface). In Java, that is not possible, so you rely on manual enforcement. Commented Oct 19, 2017 at 17:06
  • On the other hand, if you use a concrete class in Java, instead of an interface, a state invariant specification can be enforced at run-time, in the class (i.e. you can prevent a field from having an invalid value). This is not as good as compile-time enforcement, but better than nothing. The specification of the behavior for the class or interface is always separate from the implementation of that specification, since there is no contract language in Java. If you use documentation comments at least they can be collected together in the same document. Commented Oct 19, 2017 at 17:10

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.