是否存在无法编写或寻求的Stream派生类会破坏Liskov替换原则吗?
例如,无法搜索NetworkStream,如果调用方法NotSupportedException
,则会抛出Seek
。
或者因为CanSeek
标志的存在可以吗?
考虑到Square
继承自Rectangle
的众所周知的示例...将DoesHeightAffectsWidth
和DoesWidthAffectsHeight
添加到Rectangle
以解决问题?
这不是通过添加标志来打开固定东西的大门吗?
答案 0 :(得分:7)
CanSeek
从技术上讲使流类不会违反LSP。只有当它返回真实时,才会寻求工作的承诺。
我个人认为它是ISP的严重弯曲,可能是SRP,而我的内部设计师更喜欢像SeekableStream
子类/接口这样的可寻找流可以继承的东西。但我确信这带来了自己的问题(例如,在有时可寻找的流中)......坦率地说,现实世界的可用性胜过原则。
这是要记住的事情。偶尔,原则与现实相互碰撞。在大多数情况下,SOLID原则有助于最大限度地减少不必要的复杂性,并且通常可以保持OO系统的可维护性并防止它们在自身重量下崩溃。如果纯度导致系统更多复杂,但是 - 例如,因为现在只有有时可寻找的流不能很好地适应层次结构 - 那么偶尔可能会出现一些丑陋的情况。合理的。
但它永远不应该是第一选择,因为法律条文允许它。 SOLID原则不仅仅是规则;他们是原则。他们是法律背后的理念 - 精神。如果你在通过精神律师的过程中坚持这封信,那么你就错过了原则的全部要点。
至于Square / Rectangle问题......从技术上讲,确定改变高度的属性/函数是否也会改变宽度,可以考虑与LSP的字母保持一致。尽管如此,感觉就像律师一样,并且正在推动其他SOLID原则的界限。从现实的角度来看,它绝对不是最佳解决方案,因为它增加了复杂性并引入了偶然副作用的可能性;现在,想要说rect.Height = 50;
的所有内容也会无意中改变宽度。
答案 1 :(得分:5)
Can...
方法意味着Stream
不会破坏LSP。 Stream
提供功能进行读取,写入和搜索,但没有保证任何实现类都将遵守它。 Can...
方法使这成为Stream
契约的显式特征 - 派生类必须实现它们,以允许客户端代码在调用之前检查派生类是否实现某些行为。因此,尝试写入Stream
的任何代码都应该在调用CanWrite
之前检查Write
,这可以通过Stream
的任何正确实现的派生来完成。因此,它们可以互换,因为LSP需要。
我认为添加标记派生类是否实现某个特定功能的方法肯定会被滥用 - 如果一个团队没有纪律,他们最终可能会有一个非常广泛,臃肿的界面打破ISP。我认为Stream
和IList<T>
在这方面设计得很好 - 它们不会破坏LSP,并且它们定义了一个足够狭窄的密切相关行为的合同以留在ISP中。显然,他们的设计已被考虑过了。
我认为,如果Square
继承自Rectangle
,您当然可以添加DoesHeightAffectsWidth
和DoesWidthAffectsHeight
来解决问题,但团队必须决定是否可接受的,或者这些方法的添加是否会破坏ISP。增加AreAllInternalAnglesEqual
来支持梯形太远了吗?在某种程度上,这取决于编写代码的工程师。