4 ms·
Many developers, myself included, aren't fans of the Liskov Substitution Principle because base classes aren't a great way to reuse code. Prefer composition ov
by humanrebar 10y ago
Many developers, myself included, aren't fans of the Liskov Substitution Principle because base classes aren't a great way to reuse code.
Prefer composition over inheritance for reuse. And prefer interfaces over base classes (abstract or concrete) for polymorphic behavior.
There aren't many situations where Liskov is even relevant with those principles in mind. I have to inherit from a concrete type from time to time when when a third-party API requires it. And, inevitably, my design becomes more convoluted than necessary because of that.
- talideon 10y agoThat's a very narrow reading of LSP. If you're dealing purely with interfaces, LSP is still just as useful. In that case, if two types share the same interface, it should be possible to substitute objects of one type for those of the other where that interface is expected and not break the program.
- humanrebar 10y agoIn that case, it's better to phrase the guideline in terms of interfaces and implementations to avoid the digressions into ABCs. The interfaces that complicate my designs were written by well-meaning engineers that thought ABCs are best practice if the LSP is followed. That's not the case anymore and I'd rather we start teaching from a more sensible default.
- talideon 10y agoLSP came out of Liskov's work on CLU (amongst other things). CLU didn't have inheritance, though it did have have 'interface specifications'. LSP is most properly thought of as pertaining to interfaces rather than inheritance. LSP as most people seem to currently conceive it is coloured by their experience with languages like C++, where inheritance and polymorphism are welded to one another. Same goes for the likes of Java, albeit to a lesser extent. In the case of languages like that, LSP also needs to deal with the subclass/superclass relationship, and that has over time overshadowed its most important aspect: substitutability of implementations.
- humanrebar 10y ago> ...C++, where inheritance and polymorphism are welded to one another Technically there many ways to have polymorphism in C++ without inheritance and virtual methods (closures, templates, tagged unions, etc.). In practice, students learn about polymorphism from OOP textbooks and conflate polymorphism and inheritance. The work on 'concepts' for (maybe) C++20 fits more nicely with subtyping and LSP, it sounds like.
- bunderbunder 10y agoThe problems with subclass inheritance aren't a challenge to LSP. They're an illustration of exactly what LSP is about. In order to make it easier to follow LSP, it's wise to minimize the amount of concrete implementation in your base types. The more you can minimize it, the less likely you are to encounter a situation where a base type implements behavior you don't want. That in turn reduces the spots where you might even want to violate LSP. The logical end to this is purely abstract base classes which contain no implementation. These types are so easy to reuse that some languages have even enshrined them as their own special language construct (typically called "interfaces"), and given them special privileges that types which allow implementation don't get to enjoy, such as multiple inheritance. The reason they get these special privileges is that it's relatively easy for them to enjoy those features without violating LSP, whereas it's inherently tricky for types that include concrete implementation to do the same.
- StyloBill 10y agoI never thought about interfaces this way. Great comment.
- humanrebar 10y agoI wasn't challenging the LSP as much as saying it's practically moot, at least for me. To that end, the digression into ABCs and LSP only gives inexperienced developers a digression to explore on the way to cleaner design principles. It's also worth noting that ABCs are especially problematic when physical representation of data is concerned. Inheritance in C++ affects memory layout, for instance. And adding new serializeable members to a base class in any language is a reverse-incompatible change.
- bunderbunder 10y agoIf there's any physical representation, then it's not all the way there on the abstraction front. I meant to exclude any definition of storage format with the "that contain no implementation" qualifier, because specifying data members is necessarily specifying how you expect the storage of that information to be implemented. As a contrasting example, Java and .NET interfaces aren't allowed to contain fields, only method and property signatures. Which really means only method signatures, since Java and .NET properties are just methods + syntactic sugar. Both languages also have ABCs, but they are distinct language constructs that don't get to enjoy the same privileges as interfaces. That may mean that C++ abstract base classes don't quite hit it on the LSP front. It's also true that C++ is a language that predates LSP's formulation by over a decade, so we can't really expect it to be a language that's designed to take LSP into account, anyway. Long story short, though, just because LSP is baked into your programming language doesn't mean it's moot. It just means you're working in a language that's well-designed on that front.
- tel 10y agoThe huge issue is that subclassing is different from subtying. LSP is absolutely necessary for subtyping, but subtclassing is only tangentially related. In practice, this doesn't quite hold though since the principle method by which subtyping is introduced is subclassing. This unification is the source of many woes. To see an example where this doesn't hold you can take a look at OCaml. It has an object system which separates subtyping and subclassing and helps you to take note of what subclassing is actually about: reusing creation code. It ends up being very similar to normal function re-use at the end of the day and is very nice. It's just not subtyping.
- dllthomas 10y ago> Many developers, myself included, aren't fans of the Liskov Substitution Principle The way this is worded, it sounds like you would encourage violation of the principle. From the rest of the thread, it seems like you just encourage work where it does not apply. I think a lot of respondents are reacting to the former - while the latter is much less controversial (although note tel's commentary on distinction between type and class).