Liskov substitution principle and Streams
c#, liskov-substitution-principle, oop, solid-principles, stream
Solution
The `Can...` methods mean that `Stream` doesn't break LSP. `Stream` provides the capability to read, write and seek, but no guarantee that any implementing class will honour it. The `Can...` methods make this an explicit feature of the `Stream` contract - derived classes must implement them to allow client code to check whether a derived class implements certain behaviour before a call is made. So, any code that attempts to write to a `Stream` should check `CanWrite` before calling `Write`, for example, and this can be done with any correctly-implemented derivative of `Stream`. Ergo, they're interchangeable, as LSP requires.
I think it's certainly true that adding methods to flag whether or not derived classes implement a particular feature could be abused - if a team were undisciplined, they may end up with a very broad, bloated interface that breaks ISP. I think that `Stream` and `IList<T>` are well-designed in this respect - they don't break LSP, and they define a narrow enough contract of closely-related behaviours to stay within ISP. Clearly, their design has been considered.
I think that in the case of `Square` inheriting from `Rectangle`, you could certainly add `DoesHeightAffectsWidth` and `DoesWidthAffectsHeight` to fix the issue, but the team must decide whether that's acceptable, or whether the addition of those methods breaks ISP. Is the addition of `AreAllInternalAnglesEqual` to support trapezoids too far? To a certain extent, it's up to the engineers writing the code.
Problem
Does the fact that there are Stream derived classes that cannot be written or sought break the Liskov substitution principle? For example, the NetworkStream cannot be sought, it will throw a `NotSupportedException` if the method `Seek` is called. Or because the existence of `CanSeek` flag it is ok? Considering the well known example of `Square` inheriting from `Rectangle`... would the addition of the flags `DoesHeightAffectsWidth` and `DoesWidthAffectsHeight` to `Rectangle` fix the issue? Doesn't this open the door for fixing things by adding flags?