为什么我们要使用接口?它只是用于标准化吗?

81

为什么我们要使用接口?

仅仅是为了标准化吗?


1
与...相反?接口有许多用途... - Jason
6
我真的希望人们不要在那些与c#无关的帖子中打上c#等标签。这是一个非常好的问题,但由于c#被忽略了,我可能会错过它。 - Tyler Carter
12个回答

174

接口的目的

  • 创建松耦合的软件
  • 支持按约定设计(实现者必须提供整个接口)
  • 允许插件式软件
  • 允许不同的对象轻松交互
  • 隐藏类的实现细节
  • 促进软件重用

类比1:就像美国航天飞机、俄罗斯联盟号宇宙飞船和中国神舟五号都可以对接国际空间站,因为它们实现了相同的对接接口。(这只是一个例子——我不知道在现实生活中是否真的如此,但为了举例子而悬置我们的怀疑)

类比2:就像你可以将各种计算机显示器插入家用电脑一样。你可以将墙壁大小的电视机、旧式CRT(厚的那种)、20英寸平板屏幕或盲人用的点字机插入其中。这些不同设备与你的计算机具有兼容性,因为它们都遵循接口标准。

C#接口的详细信息——使用C#/OOP接口,你正在做相同类型的事情,但是在看不见/虚拟的世界中。

你关于标准化的想法是正确的,但还需要考虑灵活性、可伸缩性、可扩展性、可维护性、可重用性、可测试性和功能强大等因素。
(使用软件接口越多,这些“流行词”就越能被理解。同时,要始终考虑实际情况中的接口,因为它们同样适用于我们。)

18
你忽略了我最喜欢使用接口的方式:可测试性。如果我有两个类,A和B,且A.foo调用B.bar,只要B实现了一个接口并可以被“注入”到A中,我就可以使用模拟、伪造或存根类来替代真正的B。当A.foo根据B.bar的返回值更改其行为时,这特别有用。(假设B.bar返回一个布尔值。A.foo可能有一个if(B.bar)语句和一个else子句)。在B中使用接口允许我创建mockB、fakeB和/或stubB,从而使我能够测试B.bar返回true或false时会发生什么。 - aridlehoover
@Alan R - 已添加可测试性。谢谢。 - John K
1
被接受的答案是顶级的东西,看到这个概念没有像大多数框架设计指南材料那样被丢弃,真是令人耳目一新。只想补充一点,如果你认真使用泛型,我不知道你以前是怎么过来的。 - rama-jka toti
6
赞成“标准化”,但同时需要兼顾“灵活性、可扩展性、可扩展性、可维护性、可重用性、可测试性和实用性”。 - Ravi

30

接口用于描述一个实现的事物能做什么。因此,您可以将实现了同一接口的多个对象视为该接口类型的一种。

例如:

public interface IMyInterface{
    public void DoFirst();
    public int DoSecond();
}


public class A : IMyInterface{
   //class has to implement DoFirst and DoSecond
   public void DoFirst(){
     Console.WriteLine("Blubb1");  
   }

   public int DoSecond(){
     Console.WriteLine("Blubb2");
     return 2;  
   }
}

public class B : IMyInterface{
   //class has to implement DoFirst and DoSecond
   public void DoFirst(){
     Console.WriteLine("Blibb1");  
   }

   public int DoSecond(){
     Console.WriteLine("Blibb2");  
     return 4;
   }
}

这些类以多种方式实现接口。但是你可以将它们用作IMyInterface。 例如:

public static void DoMethodsInInterface(IMyInterface inter){
    inter.DoFirst();
    inter.DoSecond();
}


public static void main(){

   DoMethodsInInterface(new A());
   DoMethodsInInterface(new B());
   //Or use it in a List
   List<IMyInterface> interlist = new List<IMyInterface>();
   interlist.Add(new A());
   interlist.Add(new B());
   foreach(IMyInterface inter in interlist){
      inter.DoFirst();
   }

}

我希望这能让你更清楚接口的有用之处。


2
这个问题与一个问题相关联,该问题询问为什么要使用接口而不是仅拥有执行适当功能的成员等。你的答案最接近回答那个问题。如果两个不相关的类都有一个名为Woozle的方法,任何想要接受对任一类的引用并Woozle它的代码都必须知道它正在处理哪个类,并且只能Woozle它所知道的类。相比之下,如果两个类都实现了IWoozler,那么给定任何IWoozler的代码都可以Woozle它,而无需知道其确切类型。 - supercat
这个答案比我看到的任何答案都更好。它关注于一个对象能做什么。接口指定了与对象相关的一组操作,而不指定在这些操作中发生了什么,这显然是特定于对象的。 - m12lrpv

5

这是为了接口设计,让你可以在不同的设备之间进行连接。当你有多个相同设备的实现时,它非常有用。当你需要将一个接口应用于多个不同的类时,因为你需要一些约定,这些类将能够做一些事情或具有某些功能。

  • 当你有多个相同设备的实现时
  • 当你需要将一个接口应用于多个不同的类时,因为你需要一些约定,这些类将能够做一些事情或具有某些功能

4

这是一个高层次的概述...

接口在信息隐藏的概念中扮演着重要的角色。

它们基本上帮助您隐藏类的实现细节,以便调用类不依赖于该实现。因此,通过使用接口,您可以修改实现而不更改调用类。这反过来又限制了代码的复杂性,使其在长期内更易于维护。

当我开始理解接口时,它们被解释为“提供描述您的类的合同”。不确定是否会对您有所帮助,但如果您认为汽车接口可以说它驾驶刹车转向。因此,只要它能将我从A点带到B点,我就不必知道这些功能是如何实现的。


2
主要原因是像C#/ Java这样的语言不支持多重(类)继承,因此接口被使用(请参见什么是多重继承的确切问题?)。但是,允许多个(接口)实现,使得类可以以不同的方式使用。

1
在托管语言中,接口并不能替代多重继承。而且,即使在支持多重继承的语言中,没有实现的接口概念仍然是相关和有用的(参见依赖注入)。 - aridlehoover
1
-1 想得不太周全,还是你真的不懂C#或Java? - John Saunders
1
这本质上是C#和Java经验的艰难探索发现的。如果您试图通过避免多重继承来简化,只会在其他地方创建复杂性。由于缺乏一个统一的基础方法,您最终会面临更糟糕的复杂性。请参见Krzystof Cwalina在C# Programming Language第3版的1.9接口中的注释,其中确切地说明了这一点。截至目前,在C#和Java中仍存在未解决的问题,如果允许类的多重继承,则会有简单的解决方案。 - Daniel Earwicker
@DanielEarwicker:有没有办法为完整的多重继承定义语义,使得类类型变量仅封装其目标的标识,并将任何类类型变量强制转换为“Object”并返回将导致对原始目标的引用?这样的事情在多重继承接口中是可能的,但我不知道有任何适用于多重继承类的语义。 - supercat
@supercat - 或者换句话说,接口缺乏提供定义的特性。只有在存在冲突时才会出现问题,并且可以通过提供一种明确解决冲突的方式来解决。似乎与您之前的问题无关? - Daniel Earwicker
显示剩余7条评论

2

接口有些尴尬。它们通过相信相同的名称和实现的接口意味着相同的行为来支持契约设计。这仅得益于API文档,必须经过人工检查。这使得接口太弱。解决这个问题的一种方法可能是正式规范。 另一方面,接口太强、太严格了。您无法演变接口,这经常妨碍重用。这通过协议来解决-动态语言中的机制,发送消息(调用方法),当接收器不支持该消息时,会调用标准回调。 具有约束条件的具体协议可能更好。


1

思考远程调用...

这里涉及到客户端和服务器。假设它们被互联网物理隔离。客户端调用一个方法,其实际执行发生在服务器上。从客户端的角度来看,客户端不知道执行操作的服务器对象的任何信息。但是它知道要调用哪个方法。因为在构建客户端程序时,我们只暴露了一个接口(或契约)。我们没有暴露实际存在于服务器上的整个对象。尝试在 .net 远程调用中进行一些演示应用程序,您就会明白剩下的部分。祝编程愉快。


0

0
为什么我们要使用接口?
一些语言使用虚函数表实现多态方法调用,并且丢弃大部分类型信息,这使得不定义接口变得困难。
因此,有时我们只是因为语言设计需要而使用接口。

0
接口将数据类型与实现逻辑分离。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接