Liskov替代原理和接口 [英] Liskov substitution principle and Interface

查看:80
本文介绍了Liskov替代原理和接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

数组的ICollection<T>.Add()实现是否违反了Liskov替换原理?该方法导致NotSupportedException,它会破坏LSP,即恕我直言.

Does the ICollection<T>.Add()-implementation of arrays break the Liskov substitution principle? The method results in a NotSupportedException, which does break the LSP, IMHO.

string[] data = new string[] {"a"};
ICollection<string> dataCollection = data;
dataCollection.Add("b");

这导致

未处理的异常:System.NotSupportedException:集合的 固定大小.

Unhandled exception: System.NotSupportedException: Collection was of a fixed size.

我发现了一个关于Stream实现的非常相似的问题.我打开一个单独的问题,因为这种情况非常不同: Liskov替代原理和流 . 区别在于ICollection不提供CanAdd属性或类似Stream类的东西.

I found a pretty similar question concerning Stream-implementations. I open a separate question, because this case is pretty different: Liskov substitution principle and Streams. The difference here is that ICollection does not provide a CanAdd-Property or such thing, as the Stream-class does.

推荐答案

我明白了为什么会这样.有一个函数需要一个集合,并且期望它是可修改的.传递数组会使其失败,所以很明显,您不能用此特定实现替代接口,对吧?

I can see why you'd think so. There's a function that expects a collection, and it expects it to be modifiable. Passing an array will make it fail, so clearly you can't substitute the interface with this particular implementation, right?

有问题吗?可能是.这取决于您期望抱抱理想的频率.您是否要不小心使用数组而不是集合,然后在十年后惊讶于它崩溃了?并不真地. .NET应用程序使用的类型系统并不完美-它不会告诉您这种特殊的ICollection<T>使用情况要求集合可以修改.

Is it a problem? Maybe. It depends on how often you expect ideals to hold. Are you going to use an array instead of a collection by accident and then be surprised ten years later that it breaks down? Not really. The type system .NET applications use isn't perfect - it doesn't tell you this particular ICollection<T> usage requires the collection to be modifiable.

如果数组不假装实现ICollection<T>(或IEnumerable<T>,它们也不真正"实现),. NET会更好吗?我不这么认为.有没有办法保持阵列存在" ICollection<T>的便利性,而这又可以避免相同的LSP冲突?没有.基础数组仍将是固定长度的-充其量,您将违反更有用的原则(例如,不应期望引用类型具有引用透明性).

Would .NET be better off if arrays didn't pretend to implement ICollection<T> (or IEnumerable<T>, which they also don't "really" implement)? I don't think so. Is there a way to keep the convenience of arrays "being" ICollection<T> that would also avoid the same LSP violation? Nope. The underlying array would still be fixed-length - at best, you'd be violating more useful principles instead (like the fact that reference types are not expected to have referential transparency).

但是等等!让我们看一下ICollection<T>.Add的实际合同.是否允许抛出NotSupportedException?哦,是的-引用MSDN:

But wait! Let's look at the actual contract of ICollection<T>.Add. Does it allow for a NotSupportedException to be thrown? Oh yes - quoting MSDN:

[如果出现...,则抛出NotSupportedException.] ICollection是只读的.

[NotSupportedException is thrown if ...] The ICollection is read-only.

当您查询IsReadOnly时,数组的确返回true.合同得到维护.

And arrays do return true when you query IsReadOnly. The contract is upheld.

如果您认为Stream不会因为CanWrite而中断LSP,则必须将数组视为有效集合,因为它们具有IsReadOnly,并且它是true.如果一个函数接受一个只读集合并尝试添加到该集合中,那么这是该函数中的错误.无法在C#/.NET中明确指定此名称,因此您必须依赖合同的其他部分,而不仅仅是类型-例如该函数的文档应指定为只读集合抛出NotSupportedException(或ArgumentException或其他内容).一个好的实现将在功能开始时进行此测试.

If you consider Stream not to break LSP because of CanWrite, you must consider arrays to be valid collections, since they have IsReadOnly, and it is true. If a function accepts a read-only collection and tries adding to it, it's an error in the function. There's no way to specify this explicitly in C#/.NET, so you have to rely on other parts of the contract than just types - e.g. the documentation for the function should specify that a NotSupportedException (or ArgumentException or whatever) is thrown for a collection that is readonly. A good implementation would do this test right at the start of the function.

要注意的重要一件事是,在C#中,类型并不像在定义LSP的类型理论中那样受约束.例如,您可以在C#中编写这样的函数:

One important thing to note is that types aren't quite as constrained in C# as in the type theory where LSP is defined. For example, you can write a function like this in C#:

bool IsFrob(object bobicator)
{
  return ((Bob)bobicator).IsFrob;
}

bobicator可以用object的任何超类型代替吗?显然不是.但这显然不是Frobinate类型差的问题-这是IsFrob函数中的错误.实际上,许多用C#(和大多数其他语言)编写的代码仅能处理比方法签名中的类型所指示的对象更受约束的对象.

Can bobicator be substituted with any supertype of object? Clearly not. But it just as clearly isn't a problem of the poor Frobinate type - it's an error in the IsFrob function. In practice, a lot of code in C# (and most other languages) only works with objects far more constrained than would be indicated by the type in the method signature.

对象仅在违反其超类型的约定时才违反LSP.它不能对 other 代码违反LSP负责.通常,您会发现制作在LSP下不能完全保存的代码非常实用-工程学一直是折衷方案.仔细权衡一下费用.

An object only violates the LSP if it violates the contract of its supertype. It cannot be responsible for other code violationg LSP. And often you'll find it quite pragmatic to make code that doesn't perfectly hold up under LSP - engineering is, and always has been, about trade-offs. Weigh the costs carefuly.

这篇关于Liskov替代原理和接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆