我知道它们强制你去实现方法等,但我不明白的是为什么你会想要使用它们。有没有人可以给我一个好的例子或解释,告诉我为什么我要实现这个。
我理解他们强制你去实现方法等,但我无法理解为什么你想要使用它们。能否有人给出一个好的例子或者解释,告诉我为什么我要实现这些内容。我知道它们强制你去实现方法等,但我不明白的是为什么你会想要使用它们。有没有人可以给我一个好的例子或解释,告诉我为什么我要实现这个。
我理解他们强制你去实现方法等,但我无法理解为什么你想要使用它们。能否有人给出一个好的例子或者解释,告诉我为什么我要实现这些内容。一个具体的例子: 接口是一种很好的方式,用于指定其他人的代码必须满足的合同。
如果我在编写一个代码库,我可能会编写针对具有某些行为的对象有效的代码。最好的解决方案是在接口中指定这些行为(没有实现,只是描述),然后在我的库代码中使用实现该接口的对象的引用。
然后,任何随机的人都可以创建一个实现该接口的类,实例化该类的对象并将其传递给我的库代码并期望它能够工作。注意:当然可以严格实现一个接口,同时忽略接口的意图,因此仅实现一个接口并不能保证事情能够正常工作。愚蠢总能找到办法! :-)
另一个具体的例子: 两个团队分别开发不同组件并需要相互协作。如果两个团队在第一天坐下来并就一组接口达成一致,那么他们可以各自实现基于这些接口的组件。A团队可以构建模拟B团队组件的测试工具,并反之亦然。这样进行并行开发,减少错误。
关键点在于接口提供了一层抽象,以便你编写的代码忽略不必要的细节。
大多数教科书中使用的经典例子是排序程序。只要你有办法比较任意两个对象,就可以对任何类的对象进行排序。因此,通过实现IComparable
接口,您可以使任何类都可排序,该接口强制您实现比较两个实例的方法。所有排序例程都编写为处理对IComparable对象的引用,因此一旦您实现了IComparable,就可以在您的类对象集合上使用任何这些排序例程。
理解接口最简单的方式是,它们允许不同的对象公开共同的功能。这使得程序员可以编写更简单、更短的代码来适配接口,只要对象实现了该接口,程序就能够正常工作。
例子1: 有许多不同的数据库提供商,例如MySQL、MSSQL和Oracle等。然而,所有的数据库对象都可以执行相同的操作,因此你会发现许多针对数据库对象的接口。如果一个对象实现了IDBConnection,则它将暴露Open()和Close()方法。所以,如果我想让我的程序在不同的数据库提供商中运行,我就需要适配接口而不是特定的提供商。
IDbConnection connection = GetDatabaseConnectionFromConfig()
connection.Open()
// do stuff
connection.Close()
通过编程使用接口(IDbconnection),我现在可以在配置中更换任何数据提供程序,而我的代码保持完全相同。这种灵活性非常有用且易于维护。但缺点是我只能执行“通用”数据库操作,可能无法充分利用每个特定提供程序所提供的优势。因此,在编程中,您必须进行权衡并确定哪种情况对您最有利。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
}
你可以看到,即使这些动物以不同的方式执行每个动作,我仍然可以针对它们所有人统一的模型进行编程,这是接口的许多好处之一。因此,接口最重要的事情就是对象有哪些共同点,以便您可以以相同的方式针对不同的对象进行编程。这样可以节省时间,创建更加灵活的应用程序,隐藏复杂性/实现细节,模拟真实世界的对象/情况,同时还有许多其他的好处。希望这可以帮助你。接口定义契约,这是关键词。
当您需要在程序中定义合同但实际上不太在意满足该合同的类的其余属性时,可以使用接口。
因此,让我们看一个例子。假设您有一个提供对列表进行排序功能的方法。首先..什么是列表?您真的在乎它保存哪些元素以便排序吗?您的答案应该是否定的...在.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:我知道这些例子有点幼稚,但它们只是示例...
IComparable
的契约并没有规定你需要返回 1
,0
和 -1
,而是要求你返回 >0
,0
,和 <0
。这样可以进行一些优化,比如只需通过 return this.intNum - CompareWith.intNum
来对 int 进行排序。你不关心它相对于 0 的差距有多少,只关心它是更大还是更小。 - Scott ChamberlainWhen you need different classes to share same methods you use Interfaces.
接口在期望充分利用多态的面向对象系统中是必不可少的。
一个经典的例子可能是IVehicle(交通工具接口),它有一个Move()方法。你可以有Car、Bike和Tank类,它们实现了IVehicle。它们都可以Move(),而且你可以编写代码,不关心处理哪种类型的车辆,只要它能Move()就行。
void MoveAVehicle(IVehicle vehicle)
{
vehicle.Move();
}
想象一个基本的接口,它定义了一个基本的CRUD机制:
interface Storable {
function create($data);
function read($id);
function update($data, $id);
function delete($id);
}
class Logger {
Storable storage;
function Logger(Storable storage) {
this.storage = storage;
}
function writeLogEntry() {
this.storage.create("I am a log entry");
}
}
这个记录器不在乎你传递的是数据库连接还是操作磁盘上文件的东西。它只需要知道可以在其上调用create(),那么它就会按预期工作。
接下来出现的问题是,如果数据库和CSV文件等都可以存储数据,那么它们是否应该从通用的可存储对象中继承,从而摆脱接口的需求呢?答案是否定的...并非每个数据库连接都能实现CRUD操作,每个文件读取器也是如此。
接口定义了对象能做什么以及您需要如何使用它...而不是定义它是什么!
有很多理由这么做。当你使用一个接口时,如果未来需要重构或重写代码,你就已经准备好了。同时,你还可以为简单操作提供一种标准化的API。
例如,如果你想编写一个类似快排的排序算法,那么你只需要能够成功比较两个对象即可对任何对象列表进行排序。如果你创建一个名为ISortable的接口,那么任何创建对象的人都可以实现ISortable接口并使用你的排序代码。
如果你正在编写使用数据库存储的代码,并且你写入了一个存储接口,你可以在以后替换掉这段代码。
接口鼓励代码的松耦合,使你拥有更大的灵活性。