接口是否可以完全设计而不考虑实现?
例如,在工作中我被告知要设计一个界面。但后来我找到了 接口取决于实现细节。他们一直告诉我 你可以完全设计界面而不考虑实现, 但我反对这一点。这就像为火箭创建一个界面, 并为火箭添加一个飞向火星的功能。它没有 感觉添加这个功能,而不考虑你是否能真正做到这一点,飞到火星! 所以我认为,界面设计并不完全独立于实现细节。
答案 0 :(得分:2)
Fly是一种界面行为。
但是火箭如何飞行?
这是你在具体课上的实施。
“AbleToFly”界面可以由鸟类,飞机和火箭实施,但实际的实施取决于具体的类别。
答案 1 :(得分:0)
接口合同的设计是一种平衡;良好的界面应满足消费者的需求,但阻止实施使用最有效的数据结构和算法,以满足消费者的实际需求。如果消费者基本从了解接口实现的某些事情中获益,则可能值得在接口合同中包含此类详细信息,但前提是消费者的利益将超过实施者的任何成本。此外,在设计界面时要注意如何实现它,这一点很重要。人们不需要充实实现的所有细节,但是对潜在实现的理解以及哪些特性可能便宜或昂贵可能允许接口比其他方式更好地满足客户端。
例如,考虑.NET IList<T>
接口,它可以追溯到.NET泛型的最开始。尽管典型的实现将由一个或多个数组支持,并且可以非常有效地将一系列元素从一个数组复制到另一个数组,但是将一系列元素从一个IList<T>
复制到另一个数组的唯一通用方法是单独枚举它们 - 这种方法比数组副本慢很多倍。如果存在系统定义的非可继承(*)ReadonlyArraySegment
类型 - 类似ArraySegment
- 封装了数组引用和一系列元素,但不像ArraySegment
- 不允许外部代码访问数组本身,然后IList<T>
可以提供一个方法,该方法将返回列表的一部分作为IEnumerable<ReadonlyArraySegment<T>>
,以及一个方法 - 给定{{1可以使用IEnumerable<ReadonlyArraySegment<T>>
将数据范围复制到自己的后备存储中。如果Array.Copy
的两个实现都碰巧使用单个数组作为后备存储,则可以非常有效地复制项目。如果他们使用多个数组作为后备存储,复制将更复杂,但仍然比单独处理元素快许多倍。如果源列表使用的东西不是基于数组(例如树),IList<T>
最终必须将每个列表项复制到单个元素数组[取决于合同的编写方式,它可能能够连续回收相同的单元素阵列。
(*)如果系统类型是不可继承的,并且它的契约提供了代码无法修改封装数组或获取对它的引用,并且唯一可以用数组完成的事情可能是从中读取或复制元素,然后其他具有支持数组的数据类型可以通过IEnumerable<ReadonlyArraySegment<T>>
安全地将其内容提供给外部代码,特别是如果有一种方法可以创建{{1以后可以使它无效。
让ReadonlyArraySegment
包含一个将列表作为ReadonlyArraySegment
返回的函数在某种意义上会暴露出一个共同的实现细节(列出经常使用的支持数组),但它也可以提供一个显着的好处对客户(将数据从一个IList<T>
快速复制到另一个)的能力。至于微软是否考虑过这些功能,并认为它们不值得付出代价,或者他们只是希望得到一个可以做出最重要的事情的接口,或介于两者之间的某个地方,我不知道。我的观点是,有时接口合同可能有助于公开可能的实现细节;这种风险通常应该以这样的方式进行,即以其他方式实施事物的客户仍然可以满足合同(尽管可能不如以预期方式实施事物的那些),并且通常仅在客户实际上从中受益时才适用