在我的开发学习中,我觉得我必须学习更多关于接口的知识。
我经常读到它们,但我似乎无法理解它们。
我读过这样的例子:动物基类,IAnimal接口的东西,如“Walk”,“Run”,“GetLegs”等-但我从来没有工作过,觉得“嘿,我应该在这里使用接口!”
我错过了什么?为什么这个概念对我来说这么难理解!我只是害怕这样一个事实,我可能从来没有意识到一个具体的需要-主要是由于一些缺失的理解他们!这让我觉得我作为一名开发人员缺少了一些东西!如果有人有过这样的经历,并取得了突破,我会很感激一些关于如何理解这个概念的建议。谢谢你!
它解决了一个具体的问题:
你有a b c d四种不同类型。在你的代码中,你可以这样写:
a.Process();
b.Process();
c.Process();
d.Process();
为什么不让他们实现IProcessable呢
List<IProcessable> list;
foreach(IProcessable p in list)
p.Process();
当你添加50种类型的类,它们都做同样的事情时,这种伸缩性会更好。
另一个具体问题是:
你有没有看过System.Linq.Enumerable?它定义了大量的扩展方法,可以对实现IEnumerable的任何类型进行操作。因为任何实现IEnumerable的东西基本上都在说“我支持无序foreach类型模式中的迭代”,所以你可以为任何可枚举类型定义复杂的行为(Count、Max、Where、Select等)。
一些非编程示例可能帮助您了解接口在编程中的适当使用。
There's an interface between electrical devices and the electricity network - it's the set of conventions about the shape of the plugs and sockets and the voltages/currents across them. If you want to implement a new electrical device, as long as your plug follows the rules it will be able to get services from the network. This makes extensibility very easy and removes or lowers the costs of coordination: you don't have to notify the electricity provider about how your new device works and come to a separate agreement about how to plug your new device into the network.
各国都有标准的铁路轨距。这使得铺设铁轨的工程公司和建造在这些铁轨上运行的列车的工程公司之间实现了分工,并使铁路公司有可能在不重新设计整个系统的情况下更换和升级列车。
The service a business presents to a client can be described as an interface: a well defined interface emphasises the service and hides the means. When you put a letter in a mailbox, you expect the postal system to deliver the letter within a given time but you have no expectations about how the letter is delivered: you don't need to know, and the postal service has the flexibility to choose the means of delivery that best meets the requirements and current circumstances. An exception to this is the ability of customers to choose airmail - that's not the kind of interface a modern computer programmer would have designed, since it reveals too much of the implementation.
来自自然的例子:我不太喜欢eats(), makesSound(), moves()等例子。它们确实描述了行为,这是正确的,但它们没有描述交互以及它们是如何被启用的。在自然界中,使相互作用成为可能的界面的一个明显的例子是与繁殖有关的,例如一朵花为蜜蜂提供了一个特定的界面,以便授粉能够发生。
假设你想要模拟当你试图睡觉时可能发生的烦恼。
接口前的模型
class Mosquito {
void flyAroundYourHead(){}
}
class Neighbour{
void startScreaming(){}
}
class LampJustOutsideYourWindow(){
void shineJustThroughYourWindow() {}
}
正如你清楚地看到的,当你试图睡觉时,许多“事情”都可能令人讨厌。
使用没有接口的类
但是在使用这些类时,我们遇到了一个问题。他们毫无共同之处。您必须分别调用每个方法。
class TestAnnoyingThings{
void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
if(mosquito != null){
mosquito.flyAroundYourHead();
}
if(neighbour!= null){
neighbour.startScreaming();
}
if(lamp!= null){
lamp.shineJustThroughYourWindow();
}
}
}
带有接口的模型
为了克服这个问题,我们可以引入一个iterface
interface Annoying{
public void annoy();
}
并在类中实现它
class Mosquito implements Annoying {
void flyAroundYourHead(){}
void annoy(){
flyAroundYourHead();
}
}
class Neighbour implements Annoying{
void startScreaming(){}
void annoy(){
startScreaming();
}
}
class LampJustOutsideYourWindow implements Annoying{
void shineJustThroughYourWindow() {}
void annoy(){
shineJustThroughYourWindow();
}
}
接口使用
这将使这些类的使用更容易
class TestAnnoyingThings{
void testAnnoyingThinks(Annoying annoying){
annoying.annoy();
}
}
In my experience the driving force to create interfaces didn't occur until I start doing unit testing with a mocking framework. It became abundantly clear that using interfaces was going to make mocking much easier (since the framework depended on the methods being virtual). Once I started I saw the value of abstracting away the interface to my class from the implementation. Even if I don't create an actual interface, I try now to make my methods virtual (providing an implicit interface that can be overridden).
我发现还有许多其他原因可以加强重构到接口的良好实践,但是单元测试/模拟的事情提供了最初的“顿悟时刻”的实践经验。
EDIT: To clarify, with unit testing and mocking I always have two implementations -- the real, concrete implementation and an alternate mock implementation used in testing. Once you have two implementations, the value of the interface becomes obvious -- deal with it in terms of the interface so you can replace the implementation at any time. In this case I'm replacing it with a mock interface. I know that I can do this without an actual interface if my class is constructed properly, but using an actual interface reinforces this and makes it cleaner (clearer to the reader). Without this impetus, I don't think I would have appreciated the value of interfaces since most of my classes only, ever have a single concrete implementation.