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?

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
user1787812
  • 739
  • 7
  • 16
  • 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 Oct 18 '17 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](https://github.com/shamhub/Java_programming/blob/master/0_Java_basics/Language_features/cs61b/homework4/DList.java#L26) of a class abstraction – user1787812 Oct 18 '17 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. – Frank Hileman Oct 19 '17 at 01:22
  • Without the encapsulation of the base class state mutations, type substitution, preserving contracts, is not even possible. – Frank Hileman Oct 19 '17 at 01:23
  • @DavidArno Liskov's work is all about encapsulation. – Frank Hileman Oct 19 '17 at 17:17
  • @FrankHileman, absolutely not. You belittle the breadth of the achievements of her work, including early ideas on generics, avoiding OO-style inheritance and why goto is so bad to name just a few. The LSP relates to her work on functional correctness in type hierarchies. However, it is true that she presented the principle in a paper entitled "*Data abstraction and hierarchy*", which also contained ideas on pre and post conditions and invariants. – David Arno Oct 19 '17 at 17:35
  • @DavidArno without encapsulation, you cannot have state invariants. I am not sure what you are saying. Everything I have seen that she has written about using the CLU language, and later languages, uses encapsulation. Without encapsulation, invariants just don't exist. – Frank Hileman Oct 20 '17 at 18:41
  • @DavidArno Encapsulation does not necessarily mean using an object oriented language. It means that the state of something, object, component, some system, is protected from external changes. Without such protection, you cannot have state invariants. For example, an invariant on a class A state requires that all modifications of that state take place within methods on that class, not in external classes, and not in derived classes. These methods on class A prevent invalid states from occurring, hence we enforce an invariant. A language with compiler invariant protection might work differently. – Frank Hileman Oct 20 '17 at 18:50
  • With regards to the original question, if a function has state, then that function is a type of object, for all practical purposes. There are languages that provide state to a function without using the word object, but within the implementation of such a language, you will see an object representing that state. – Frank Hileman Oct 20 '17 at 19:22

3 Answers3

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.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • How an instance fulfill the contractual obligation? We express this by writing a class. So, class definition will ensure contractual obligation. – user1787812 Oct 18 '17 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. – Robert Harvey Oct 18 '17 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 – user1787812 Oct 18 '17 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. – Robert Harvey Oct 18 '17 at 16:39
  • Then, what does correctness mean, in my query? – user1787812 Oct 18 '17 at 17:03
  • [Correctness](https://en.wikipedia.org/wiki/Correctness_(computer_science)) means that code does what it is supposed to do; that it implements the desired behavior in accordance with the stated requirements. – Robert Harvey Oct 18 '17 at 17:06
  • Negative vote from me, for your latest comment. – user1787812 Oct 18 '17 at 17:54
  • @user1787812: Read the [Wikipedia article on correctness](https://en.wikipedia.org/wiki/Correctness_(computer_science)), or provide proof that your personal definition of "correctness" is better. I'm not making any assertions that are not already well-defined elsewhere. I do, however, strongly suspect that you're overthinking all this. – Robert Harvey Oct 18 '17 at 18:49
  • In fact, Liskov never expected people to be able to break base class invariants with derived classes. Encapsulation was supposed to prevent that, and her language (CLU) separated abstraction (specification) from implementation. – Frank Hileman Oct 19 '17 at 01:16
  • @user1787812 The word "contract" refers to the formal or informal specification that includes invariants. – Frank Hileman Oct 19 '17 at 01:17
  • "Correctness", in formal verification, in regards to a component such as a class, refers to the contract for that component. Whether that fits into the total correctness of the program is a different issue. – Frank Hileman Oct 19 '17 at 01:19
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.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • 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. – Robert Harvey Oct 18 '17 at 16:47
  • With complete encapsulation of the base class, it is impossible to break the base class state invariants. – Frank Hileman Oct 19 '17 at 01: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.

Frank Hileman
  • 3,922
  • 16
  • 18
  • I think, I mentioned about contract [here](https://github.com/shamhub/Java_programming/blob/master/1_OO_Design/4_class_vs_object.odt), on page 3 – user1787812 Oct 19 '17 at 03: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. – Frank Hileman Oct 19 '17 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. – Frank Hileman Oct 19 '17 at 17:10