The Liskov Substitution Principle is a particular definition of the subtyping relation. It was introduced by Barbara Liskov in her keynote address at a conference in 1988. It describes the behavioral aspects of subtyping. Liskov describes her theory of behavioral subtyping as a form of strong behavioral subtyping.
Behavioral subtyping
The Liskov Substitution Principle is a fundamental premise of Object-Oriented Programming, and it states that a subtype of an object must be interchangeable with its parent. This means that subtypes cannot have stronger pre and post-conditions than their supertype. The principle also requires that all properties of a supertype should be present in a subtype.
Behavioral subtyping is a generalization of Hoare subtyping. It considers the interplay between the subtyping and the pre and post-conditions of the object. It also defines the substitutability of objects of different types, which means that objects of type T can be replaced by objects of type S, without changing the desirable properties of the program.
The Liskov Substitution Principle is one of the five SOLID principles of object-oriented design. It states that object-oriented feature designs should be based on the behavior of their superclass and subtypes. Using this principle in software development can reduce the amount of code smell and limit code duplication.
Behavioral subtyping according to the Liskov Substitution Principle involves the use of subtypes in software development. In this way, the Liskov Substitution Principle protects the integrity of the system. This principle is applied to different types of software. For example, an object of class FileSystem is not compatible with ReadOnlyFileSystem. In such a case, the object of type FileSystem inherits the deleteFile method.
Liskov’s Subtype Principle requires that method return types must have covariance with the supertype. This ensures that types always keep in order from generic to specific. For example, the return value of a subtype can be more specific than the original return type.
The Liskov Subtyping Principle also applies to inheritance hierarchies. Essentially, it states that the base class must comply with the contract between its derived classes and base classes. If the base class doesn’t satisfy the contract, then the derived classes can’t be a subtype of its parent.
Contravariance
Contravariance is similar to covariance, but it’s about how types are treated when used in method parameters and return values. This relationship is illustrated in Figure 7-6. This example shows how a generic type can become a contravariant by implementing a generic type parameter.
The substitution principle is useful when objects have different types. A narrow input pipe can be replaced by a wide output pipe. In this example, the upper green flanged pipe is a good replacement for the supertype, while the lower orange flanged pipe isn’t useful. However, the substitution principle implies that one function is derived from another. The higher-derived type is more closely related to the original, while the lower-derived type is less closely related to it.
The principle is also applicable to inheritance, which is another important component of SOLID design. The subclass inherits the methods, fields, and contracts of the parent class. It also inherits the preconditions and postconditions, as well as the data invariants. However, a subclass may change its contracts. The Liskov Substitution Principle allows some changes to be made to the contract, but requires that a subclass maintain certain characteristics.
The Liskov Substitution Principle is commonly expressed as a set of pre and postconditions. The subtypes may have weaker or stronger pre and postconditions. As long as the subtypes are usable by existing clients, the Liskov Substitution Principle is applicable to them.
The principle requires that the subtype should be compatible with its supertype. This means that the subtype must support all operations of the supertype. In addition, methods must not throw new exceptions. In order to satisfy this principle, a subclass must be able to change its parameters and return values.
Under the LSP, the types of method arguments and method returns must be covariant. A subtype may also override another method in its superclass and take the parent’s type as a parameter. In general, the subtypes of methods may be more general than the superclass. Nevertheless, the methods must follow the same covariance rules that apply to the superclass.
Covariance
To fully understand the Liskov substitution principle, it is important to understand the concept of covariance and contravariance. These are the terms used to describe the expected behavior of subtypes within a class hierarchy. This concept helps developers create code that is easy to understand, maintain, upgrade, and modify.
Liskov substitution rules are based on the assumption that a subclass should inherit its parent’s methods, properties, fields, and contracts. They are expected to preserve all preconditions, postconditions, and data invariants. But, in some cases, a subclass may want to change a field’s contract. In such cases, the subclass should respect the data invariant and should make sure to add a guard clause to prevent direct access.
The principle also applies to base types. As a general rule, base types should be interchangeable. Thus, if a base class has a type S, then it is a subtype of T. And vice versa. Similarly, a subtype S should be substitutable for a base class T.
Another example of the principle’s contravariance is in the case of a class that has more than one type of parameter. For example, an animal class might want to support more than one type of food, so it can extend its base method to handle more diverse food types.
The Liskov Substitution Principle is similar to Bertrand Meyer’s design by contract concept in that it enforces the contract rules and variance rules. It defines the expected behavior of subtypes within a class hierarchy. Inheritance supports this principle and is more effective than typical type theory subtyping.
Liskov’s notion of a behavioral subtype
Liskov’s notion of a behavior is an extension of Hoare’s notion of a subtype. It takes into account interactions between subtyping and preconditions and invariants. Its central argument is that substitution can be applied to behavioral subtypes.
As long as a subtype is substitutable for its supertype, it should behave like it. This implies that subtypes should share some properties of the supertype, but may also have additional ones. In a behavioral subtype, all of its properties must be present.
Liskov’s notion of a behavior subtype has a number of implications for programming. It is a key principle of Object-Oriented programming (OOP) like C++. It requires that objects be substitutable for base classes. In other words, objects of type T can be replaced with those of type S.
This concept is stronger than the usual type theory subtyping. Typical type theory subtyping relies on the covariance of return types and parameter types. However, behavior subtyping is undecidable in general. If a method in a class has the property “method for x always terminates”, then a compiler or program cannot verify that it does. Despite these limitations, behavioral subtyping can be useful in class hierarchies.