我正处于开发学习的某个阶段,我觉得我必须更多地了解接口.
我经常阅读它们,但似乎我无法掌握它们.
我已经阅读过这样的例子:动物基类,IAnimal界面,如'Walk','Run','GetLegs'等等 - 但我从来没有做过某些事情,感觉就像"嘿我应该使用界面这里!"
我错过了什么?为什么我要掌握这么难的概念!我只是因为我可能没有意识到对一个人的具体需求而感到害怕 - 主要是由于理解它们的一些缺失方面!这让我觉得自己在成为开发者方面缺少一些东西!如果有人有过这样的经历并取得了突破,我会很感激如何理解这个概念.谢谢.
它解决了这个具体问题:
你有4种不同类型的a,b,c,d.在你的代码中你有类似的东西:
a.Process(); b.Process(); c.Process(); d.Process();
为什么不让它们实现IProcessable,然后呢
Listlist; foreach(IProcessable p in list) p.Process();
当你添加50种类型的类都会做同样的事情时,这会扩展得更好.
另一个具体问题:
你有没有看过System.Linq.Enumerable?它定义了大量的扩展方法,它们可以在任何实现IEnumerable的类型上运行.因为实现IEnumerable的任何东西基本上都说"我支持无序foreach类型模式中的迭代",所以您可以为任何可枚举类型定义复杂行为(Count,Max,Where,Select等).
我非常喜欢吉米的答案,但我觉得我需要添加一些东西.整个事情的关键是对的iProcess"能够" 能.它表示实现接口的对象的功能(或属性,但意味着"内在质量",而不是C#属性意义上).IAnimal可能不是一个很好的接口示例,但IWalkable可能是一个很好的接口,如果你的系统有许多可以走的东西.您可能拥有源自动物的类,如狗,牛,鱼,蛇.前两个可能会实现IWalkable,后两个不会走路,所以他们不会.现在你问"为什么不只是拥有另一个超类,WalkingAnimal,狗和牛来自哪里?".答案是当你有一些完全在继承树之外的东西也可以走路时,比如机器人.Robot会实现IWalkable,但可能不会派生于Animal.如果你想要一个可以走路的东西列表,
现在用更多软件 - 例如IPersistable替换IWalkable,这个类比变得更接近你在真实程序中看到的东西.
当相同功能的实现不同时使用接口.
需要共享通用的具体实现时,请使用抽象/基类.
想象一个像合同这样的界面.这是一种说法,"这些类应遵循这些规则."
所以在IAnimal示例中,它是一种说法,"我必须能够在实现IAnimal的类上调用Run,Walk等."
为什么这有用?您可能希望构建一个函数,该函数依赖于您必须能够在对象上调用Run和Walk这一事实.您可以拥有以下内容:
public void RunThenWalk(Monkey m) { m.Run(); m.Walk(); } public void RunThenWalk(Dog d) { d.Run(); d.Walk(); }
...并对所有你知道可以跑步和走路的物体重复一遍.但是,使用IAnimal接口,您可以按如下方式定义函数:
public void RunThenWalk(IAnimal a) { a.Run(); a.Walk(); }
通过对接口进行编程,您基本上信任类来实现接口的意图.所以在我们的例子中,我的想法是"我不关心他们如何奔跑和行走,只要他们跑步和行走.只要他们履行协议,我的RunThenWalk就会有效.它完全正常运行而不知道其他任何事情.班级."
在这个相关问题上也有很好的讨论.
别担心.很多开发人员很少需要编写接口.您将经常使用.NET框架中可用的接口,但如果您觉得不需要很快编写一个接口,那么就没什么可惊讶的了.
我总是给某人的例子是你有一个帆船类和一个Viper类.他们分别继承了Boat类和Car类.现在说你需要循环遍历所有这些对象并调用它们的Drive()
方法.虽然您可以编写如下代码:
if(myObject is Boat) ((Boat)myObject).Drive() else if (myObject is Car) ((Car)myObject).Drive()
写起来会简单得多:
((IDrivable)myObject).Drive()
当你希望能够将单个变量用于多种类型时,Jimmy说得对,但所有这些类型都通过接口声明实现了相同的方法.然后你可以在接口类型变量上调用它们main方法.
然而,使用接口的第二个原因.当项目架构师与实现编码器不同时,或者有几个实现编码器和一个项目管理器.负责人可以编写一大堆接口,并查看系统是否可以互操作,然后将其留给开发人员以填充实现类的接口.这是确保多人编写兼容类的最佳方法,并且可以并行执行.
我喜欢军队的比喻.
如果您是软件开发人员,音乐家或律师,警长并不在意.
你被视为士兵.
这是比较容易的中士不要与他正在与人的具体细节打扰,
把所有的人都当兵抽象(...和惩罚他们时,他们无法像那些).
人们像士兵一样行事的能力称为多态性.
接口是有助于实现多态性的软件构造.
需要抽象细节才能实现简单才能回答您的问题.
多态,在词源上意味着"多种形式",是将基类的任何子类的对象视为基类的对象的能力.因此,基类有许多形式:基类本身及其任何子类.
(..)这使您的代码更容易编写,更容易让其他人理解.它还使您的代码可扩展,因为稍后可以将其他子类添加到类型系列中,并且这些新子类的对象也可以使用现有代码.
根据我的经验,在我开始使用模拟框架进行单元测试之前,创建接口的驱动力不会发生.很明显,使用接口会使模拟更容易(因为框架依赖于虚拟方法).一旦我开始,我看到了从实现中抽象出我的类的接口的价值.即使我没有创建一个实际的接口,我现在尝试使我的方法虚拟化(提供一个可以被覆盖的隐式接口).
我发现还有许多其他原因可以强化重构接口的良好实践,但是单元测试/模拟事物提供了实际经验的初始"aha时刻".
编辑:澄清一下,通过单元测试和模拟,我总是有两个实现 - 真实的,具体的实现和测试中使用的替代模拟实现.一旦你有两个实现,接口的价值变得明显 - 在接口方面处理它,这样你就可以随时替换实现.在这种情况下,我用模拟界面替换它.我知道如果我的类构造正确,我可以在没有实际接口的情况下执行此操作,但使用实际接口可以强化这一点并使其更清晰(对读者来说更清晰).如果没有这种推动力,我认为我不会理解接口的价值,因为我的大部分课程都只有一个具体的实现.
一些非编程示例可能有助于您在编程中看到接口的适当用法.
电气设备和电网之间有一个接口 - 它是关于插头和插座形状以及它们之间的电压/电流的一系列惯例.如果您想要实施新的电气设备,只要您的插头遵循规则,它就能从网络获得服务.这使得可扩展性变得非常容易并且消除或降低了协调成本:您不必通知电力供应商您的新设备如何工作,并就如何将新设备插入网络达成单独的协议.
各国都有标准的轨距.这样就可以在放下铁轨的工程公司和建造火车的工程公司之间进行分工,这使得铁路公司可以更换和升级列车而无需重新架构整个系统.
企业向客户提供的服务可以被描述为一个界面:一个定义良好的界面强调服务并隐藏手段.当您在信箱中放一封信时,您希望邮政系统在给定时间内发送信件,但您对信件的递送方式没有任何期望:您不需要知道,邮政服务可以灵活地选择最符合要求和当前情况的交货方式.一个例外是客户选择航空邮件的能力 - 这不是现代计算机程序员设计的那种界面,因为它揭示了太多的实施.
来自大自然的例子:我不太热衷于eats(),makesSound(),move()等示例.它们确实描述了行为,这是正确的,但它们并没有描述交互以及它们是如何启用的.在自然界中实现相互作用的界面的明显示例与再生有关,例如花为蜜蜂提供某种界面以便可以进行授粉.
完全有可能以.net开发人员的身份度过一生,永远不会编写自己的界面.毕竟,我们在没有它们的情况下幸存下来几十年,我们的语言仍然是图灵完整的.
我不能告诉你为什么需要接口,但我可以列出我们在当前项目中使用它们的位置:
在我们的插件模型中,我们通过接口加载插件,并提供插件编写器的接口以符合.
在我们的intermachine消息系统中,消息类都实现了一个特定的接口,并使用该接口"解包".
我们的配置管理系统定义了用于设置和检索配置设置的界面.
我们有一个接口用于避免令人讨厌的循环引用问题.(如果你不需要,不要这样做.)
我想如果有一个规则,那么当你想在is-a关系中组合几个类时使用接口,但是你不想在基类中提供任何实现.
一个代码示例(安德鲁与我的额外目的接口的组合),也说明了为什么接口而不是不支持多重继承的语言的抽象类(c#和JAVA):
interface ILogger { void Log(); } class FileLogger : ILogger { public void Log() { } } class DataBaseLogger : ILogger { public void Log() { } } public class MySpecialLogger : SpecialLoggerBase, ILogger { public void Log() { } }
请注意,FileLogger和DataBaseLogger不需要接口(可以是Logger抽象基类).但是请考虑您需要使用强制您使用基类的第三方记录器(假设它暴露了您需要使用的受保护方法).由于该语言不支持多重继承,因此您将无法使用抽象基类方法.
底线是:尽可能使用界面以获得代码的额外灵活性.您的实施不那么紧密,因此它可以更好地适应变化.