我为什么要使用Interfaces?

时间:2008-10-27 14:51:29

标签: oop interface

我知道他们强迫你实现方法等,但我不明白为什么你会想要使用它们。任何人都可以给我一个很好的例子或解释我为什么要实现它。

20 个答案:

答案 0 :(得分:58)

一个具体的例子:接口是指定其他人的代码必须符合的合同的好方法。

如果我正在编写代码库,我可能会编写对具有某组行为的对象有效的代码。最好的解决方案是在接口中指定这些行为(没有实现,只是描述),然后在我的库代码中使用对实现该接口的对象的引用。

然后任何随机的人都可以出现,创建一个实现该接口的类,实例化该类的对象并将其传递给我的库代码并期望它能够工作。 注意:当然可以严格实现接口而忽略接口的意图,因此仅仅实现接口并不能保证事情能够正常工作。愚蠢总能找到方法! : - )

另一个具体的例子:两个团队正在处理必须合作的不同组件。如果两个团队在第1天坐下并同意一组接口,那么他们可以分别采用各种方式并围绕这些接口实现其组件。团队A可以构建测试工具,模拟团队B中的组件进行测试,反之亦然。并行开发,减少错误。

关键是接口提供了一层抽象,以便您可以编写不了解不必要细节的代码。

大多数教科书中使用的规范示例是排序例程。只要您有比较任何两个对象的方法,就可以对任何类对象进行排序。因此,您可以通过实现IComparable接口使任何类可排序,这会强制您实现比较两个实例的方法。编写所有排序例程以处理对IComparable对象的引用,因此只要实现IComparable,就可以对类的对象集合使用任何这些排序例程。

答案 1 :(得分:8)

一个典型的例子是插件架构。开发人员A编写主应用程序,并希望确保开发人员B,C和D编写的所有插件符合他的应用程序对他们的期望。

答案 2 :(得分:8)

接口定义合同,这就是关键词。

当您需要在程序中定义合同时使用接口,但是您并不真正关心满足该合同的类的其他属性,只要它能够实现。

所以,让我们看一个例子。假设您有一个方法,它提供了对列表进行排序的功能。第一件事......什么是清单?你真的关心它为了对列表进行排序所拥有的元素吗?您的答案应该是否定的......在.NET中(例如),您有一个名为IList的接口,它定义了列表必须支持的操作,因此您不关心表面下的实际细节。

回到这个例子,你真的不知道列表中对象的类......你也不在乎。如果您可以只是比较对象,也可以对它们进行排序。所以你申报合同:

interface IComparable
{
  // Return -1 if this is less than CompareWith
  // Return 0 if object are equal
  // Return 1 if CompareWith is less than this
  int Compare(object CompareWith);
}

该合同指定必须实现接受对象并返回int的方法才能进行比较。现在你已经定义了一个合同,现在你不关心对象本身,而是关注合同,所以你可以这样做:

IComparable comp1 = list.GetItem(i) as IComparable;

if (comp1.Compare(list.GetItem(i+1)) < 0)
  swapItem(list,i, i+1)

PS:我知道这些例子有点天真,但它们就是例子......

答案 3 :(得分:6)

理解接口的最简单方法是允许不同的对象公开COMMON功能。这允许程序员编写更简单,更简短的代码来编程到接口,然后只要对象实现该接口就可以工作。

示例1: 有许多不同的数据库提供程序,MySQL,MSSQL,Oracle等。但是,所有数据库对象都可以执行相同的操作,因此您将找到许多数据库对象的接口。如果一个对象实现了IDBConnection,那么它会暴露方法Open()和Close()。因此,如果我希望我的程序与数据库提供程序无关,我会编程到接口而不是特定的提供程序。

IDbConnection connection = GetDatabaseConnectionFromConfig()
connection.Open()
// do stuff
connection.Close()

通过编程访问接口(IDbconnection)我现在可以在我的配置中刷出任何数据提供程序,但我的代码保持完全相同。这种灵活性非常有用且易于维护。这样做的缺点是我只能执行“通用”数据库操作,并且可能无法充分利用每个特定提供商提供的优势,因此在编程中您需要权衡所有内容,并且您必须确定哪种方案最有利于您。

示例2: 如果您注意到几乎所有集合都实现了名为IEnumerable的接口。 IEnumerable返回一个IEnumerator,它有MoveNext(),Current和Reset()。这使C#可以轻松地在您的收藏中移动。它可以做到这一点的原因是因为它公开IEnumerable接口它知道对象公开它需要通过它的方法。这样做有两件事。 1)foreach循环现在将知道如何枚举集合和2)您现在可以将强大的LINQ exprssions应用于您的集合。接口在这里非常有用的原因是因为所有集合在COMMON中都有一些东西,它们可以被移动。每个集合可以通过不同的方式(链表与数组)进行移动,但接口的优点在于实现是隐藏的,与接口的使用者无关。 MoveNext()为您提供集合中的下一个项目,它如何做到并不重要。很好,嗯?

示例3: 当您设计自己的界面时,您只需要问自己一个问题。这些东西有什么共同之处?一旦找到对象共享的所有内容,就会将这些属性/方法抽象到接口中,以便每个对象都可以从中继承。然后,您可以使用一个界面对多个对象进行编程。

当然,我必须提供我最喜欢的C ++多态示例,即动物示例。所有动物都具有某些特征。让我们说他们可以移动,说话,他们都有一个名字。因为我刚刚确定了我的所有动物的共同点,我可以将这些特质抽象到IAnimal界面中。然后我创建了一个Bear对象,一个Owl对象和一个Snake对象,它们都实现了这个接口。您可以将实现相同接口的不同对象存储在一起的原因是接口表示IS-A重放。熊IS-A动物,猫头鹰IS-A动物,因此我可以将它们全部收集为动物。

var animals = new IAnimal[] = {new Bear(), new Owl(), new Snake()} // here I can collect different objects in a single collection because they inherit from the same interface

foreach (IAnimal animal in animals) 
{
    Console.WriteLine(animal.Name)
    animal.Speak() // a bear growls, a owl hoots, and a snake hisses
    animal.Move() // bear runs, owl flys, snake slithers
}

你可以看到,即使这些动物以不同的方式执行每个动作,我也可以在一个统一的模型中对它们进行编程,这只是Interfaces的众多好处之一。

因此,接口最重要的是对象具有的共同点,以便您可以以相同的方式对DIFFERENT对象进行编程。节省时间,创建更灵活的应用程序,隐藏复杂性/实施,模拟现实世界的对象/情况,以及许多其他好处。

希望这有帮助。

答案 4 :(得分:4)

汽车上的踏板实现了界面。我来自美国,我们在路的右侧开车。我们的方向盘位于汽车的左侧。用于从左到右手动变速器的踏板是离合器 - &gt;制动 - &gt;加速器。当我去爱尔兰时,驾驶逆转了。汽车的方向盘在右边,它们在道路的左侧行驶......但是踏板,踏板啊......它们实现了相同的界面......所有三个踏板都处于相同的顺序......因此,即使班级不同,班级操作的网络也不同,我仍然对踏板界面感到满意。我的大脑就像其他车一样,能够在这辆车上呼唤我的肌肉。

想想我们不能没有的众多非编程接口。然后回答你自己的问题。

答案 5 :(得分:3)

在面向对象的系统中,接口是绝对必要的,它希望充分利用多态性。

一个典型的例子可能是IVehicle,它有一个Move()方法。您可以拥有实施IVehicle的Car,Bike和Tank课程。它们都可以是Move(),你可以编写不关心它处理什么样的车辆的代码,只需它就可以移动()。

void MoveAVehicle(IVehicle vehicle)
{
    vehicle.Move();
}

答案 6 :(得分:3)

When you need different classes to share same methods you use Interfaces.

答案 7 :(得分:2)

接口是多态的一种形式。一个例子:

假设您要编写一些日志记录代码。日志记录将转移到某个地方(可能是文件,或主代码运行的设备上的串行端口,或套接字,或抛出/ dev / null)。您不知道在哪里:您的日志记录代码的用户需要自由决定。实际上,您的日志记录代码并不关心。它只是想要它可以写入字节的东西。

因此,您发明了一个名为“可以写入字节的东西”的接口。日志代码给出了这个接口的一个实例(可能在运行时,也许它是在编译时配置的。它仍然是多态的,只是不同的类型)。您编写了一个或多个实现该接口的类,只需更改日志代码将使用哪个类,您就可以轻松更改日志记录的位置。其他人可以通过编写自己的接口实现来更改日志记录的位置,而无需更改代码。这基本上就是多态性的含义 - 只要知道一个对象以特定的方式使用它,同时允许它在你不需要知道的所有方面变化。界面描述了您需要了解的内容。

C的文件描述符基本上是一个接口“我可以读取和/或写入来自和/或来自的字节”,几乎每种类型语言都有潜伏在其标准库中的接口:流或其他。无类型语言通常具有表示流的非正式类型(可能称为契约)。所以在实践中你几乎不必自己发明这个特定的界面:你使用语言给你的东西。

日志和流只是一个例子 - 只要你能用抽象的术语描述一个对象应该做什么,但不想把它绑定到一个特定的实现/类/无论什么,就会发生接口。

答案 8 :(得分:1)

想象一下定义基本CRUD机制的以下基本界面:

interface Storable {
    function create($data);
    function read($id);
    function update($data, $id);
    function delete($id);
}

通过此界面,您可以确定实现它的任何对象必须具有创建,读取,更新和删除数据的功能。这可以通过数据库连接,CSV文件阅读器和XML文件阅读器,或任何其他可能想要使用CRUD操作的机制。

因此,您现在可以拥有以下内容:

class Logger {
    Storable storage;

    function Logger(Storable storage) {
        this.storage = storage;
    }

    function writeLogEntry() {
        this.storage.create("I am a log entry");
    }
}

此记录器不关心您是否传入数据库连接,或者是否操作磁盘上的文件。它需要知道的是它可以在其上调用create(),并且它将按预期工作。

由此产生的下一个问题是,如果数据库和CSV文件等都可以存储数据,那么它们是否应该从通用的Storable对象继承,从而不再需要接口?答案是否定的......不是每个数据库连接都可能实现CRUD操作,这同样适用于每个文件阅读器。

接口定义了对象能够做什么以及如何使用它...而不是它是什么!

答案 9 :(得分:1)

有很多理由这样做。当您使用界面时,您将来需要重构/重写代码时已做好准备。您还可以为简单操作提供一种标准化API。

例如,如果您想编写类似快速排序的排序算法,那么您需要对任何对象列表进行排序,您可以成功地比较两个对象。如果您创建一个接口,比如说ISortable,那么创建对象的任何人都可以实现ISortable接口,并且他们可以使用您的排序代码。

如果您正在编写使用数据库存储的代码,并且您已写入存储接口,则可以直接替换该代码。

接口鼓励您更加松散地耦合代码,以便您可以获得更大的灵活性。

答案 10 :(得分:0)

首先,它们为您提供了一个附加的抽象层。您可以说“对于此函数,此参数必须是具有这些参数的这些参数的对象”。您可能还希望以某种方式抽象化术语来设置这些方法的含义,同时允许您对代码进行推理。在 duck-typed 语言中,您可以免费获得。不需要显式的语法“接口”。然而,你可能仍然会创建一组概念界面,类似于契约(如契约式设计)。

此外,界面有时用于较少“纯粹”的目的。在Java中,它们可用于模拟多重继承。在C ++中,您可以使用它们来减少编译时间。

通常,它们会减少代码中的耦合。这是件好事。

您的代码也可能更容易以这种方式进行测试。

答案 11 :(得分:0)

假设您想要跟踪一系列内容。所述集合必须支持许多内容,例如添加和删除项目,以及检查项目是否在集合中。

然后,您可以使用方法add(),remove()和contains()指定接口ICollection。

不需要知道哪种集合(List,Array,Hash-table,Red-black tree等)可以接受实现接口的对象并在不知道其实际类型的情况下使用它们的代码。

答案 12 :(得分:0)

在.Net中,我创建基类并在类以某种方式相关时从它们继承。例如,基类Person可以由Employee和Customer继承。人可能具有共同的属性,如地址字段,姓名,电话等。员工可能拥有自己的部门财产。客户拥有其他独家财产。

由于一个类只能从.Net中的另一个类继承,因此我使用接口来实现其他共享功能。有时接口由不相关的类共享。使用接口创建一个开发人员将知道的实现它的所有其他类共享的契约。我还强迫这些类实现其所有成员。

答案 13 :(得分:0)

接口,仅定义接口。稍后,您可以定义方法(在其他类上),它接受接口作为参数(或更准确地说,实现该接口的对象)。这样,您的方法可以对各种各样的对象进行操作,这些对象的唯一共性是它们实现了该接口。

答案 14 :(得分:0)

在我博客的一篇文章中,我简要介绍了界面的三个目的。

  

接口可能有所不同   用途:

     
      
  • 为同一目标提供不同的实现。典型的例子   是一个列表,可能有不同   不同的实现   性能用例(LinkedList,   ArrayList等。)。
  •   
  • 允许修改条件。例如,排序函数可以接受a   可比较的界面以便   提供任何种类的排序标准,   基于相同的算法。
  •   
  • 隐藏实施细节。这也使用户更容易   阅读评论,因为在身体   界面只有   方法,领域和评论,不久   要跳过的代码块。
  •   

以下是文章的全文:http://weblogs.manas.com.ar/ary/2007/11/

答案 15 :(得分:0)

在C#接口中,对于不共享相同基类的类的多态性也非常有用。意思是,由于我们不能有多重继承,您可以使用接口来允许使用不同的类型。它也是一种允许您在没有反射(显式实现)的情况下公开私有成员的方法,因此它可以是在保持对象模型清洁的同时实现功能的好方法。

例如:

public interface IExample
{
    void Foo();
}

public class Example : IExample
{
    // explicit implementation syntax
    void IExample.Foo() { ... }
}

/* Usage */
Example e = new Example();

e.Foo(); // error, Foo does not exist

((IExample)e).Foo(); // success

答案 16 :(得分:0)

我认为你需要对设计模式有一个很好的理解,所以看看有没有力量。

退房 Head First Design Patterns

答案 17 :(得分:0)

此处还有几个类似问题的答案:Interface vs Base Class

答案 18 :(得分:0)

我见过的最好的Java代码几乎将所有对象引用定义为接口的实例而不是类的实例。这是为灵活性和变革而设计的高质量代码的强烈标志。

答案 19 :(得分:0)

正如您所指出的,当您想强迫某人以某种格式制作界面时,界面很适合。

当数据不是某种格式时,接口很好,这意味着在代码中做出危险的假设。

例如,目前我正在编写一个将数据从一种格式转换为另一种格式的应用程序。我想强迫他们放置这些字段,以便我知道它们将存在并且将有更大的机会被正确实现。我不在乎是否有另一个版本出来并且它不能为它们编译,因为它更可能无论如何都需要数据。

由于这种情况很少使用接口,因为通常你可以做出假设,或者不是要求数据来做你需要做的事情。