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