接口-什么'

我真的不明白接口存在的原因。据我所知,这是c#中不存在的多继承的一种工作(至少我是这么被告知的)。

我所看到的是,您预定义了一些成员和函数,然后必须在类中再次重新定义它们。从而使接口成为冗余。它只是感觉像句法……嗯,垃圾对我来说(请没有冒犯的意思。Junk是指无用的东西)。

在下面的例子中,我将创建一个名为Pizza的基类,而不是一个接口。

简单示例(取自不同的堆栈溢出贡献)

public interface IPizza
{
public void Order();
}


public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}


public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
162891 次浏览

考虑到你不能在c#中使用多重继承,然后再看看你的问题。

关键是接口表示合同。任何实现类都必须拥有的一组公共方法。从技术上讲,接口只控制语法,即有什么方法,它们得到什么参数以及它们返回什么。通常它们也封装语义,尽管只是通过文档。

然后,您可以拥有一个接口的不同实现,并随意交换它们。在你的例子中,由于每个pizza实例都是IPizza,你可以在任何处理未知pizza类型实例的地方使用IPizza。任何类型继承自IPizza的实例都保证是可排序的,因为它有一个Order()方法。

Python不是静态类型的,因此类型是在运行时保存和查找的。所以你可以尝试在任何对象上调用Order()方法。只要对象有这样的方法,运行时就很高兴,可能只是耸耸肩,说»Meh。«如果它没有。在c#中不是这样的。编译器负责进行正确的调用,如果它只是一些随机的object,编译器还不知道运行时的实例是否有该方法。从编译器的角度来看,它是无效的,因为它不能验证它。(你可以用反射或dynamic关键字来做这些事情,但我想现在这样做有点过分了。)

还要注意,通常意义上的接口不一定是c# interface,它也可以是一个抽象类,甚至是一个普通类(如果所有子类都需要共享一些公共代码,这可以派上用场——然而,在大多数情况下,interface就足够了)。

Interface = contract,用于松散耦合(参见掌握)。

在Python中没有duck typing的情况下,c#依赖接口来提供抽象。如果类的依赖项都是具体类型,则不能传入任何其他使用类型的接口,可以传入实现该接口的任何类型。

接口定义了某种功能的提供者和相应的使用者之间的契约。它将实现与契约(接口)解耦。您应该了解一下面向对象的体系结构和设计。你可能想从wikipedia: http://en.wikipedia.org/wiki/Interface_(计算)开始

以下是你的例子:

public interface IFood // not Pizza
{
public void Prepare();


}


public class Pizza : IFood
{
public void Prepare() // Not order for explanations sake
{
//Prepare Pizza
}
}


public class Burger : IFood
{
public void Prepare()
{
//Prepare Burger
}
}

接口实际上是实现类必须遵循的契约,它实际上是我所知道的几乎所有设计模式的基础。

在你的例子中,创建这个接口是因为任何是一个 Pizza(意味着实现了Pizza接口)都保证已经实现了

public void Order();

在你提到的代码之后,你可以这样写:

public void orderMyPizza(IPizza myPizza) {
//This will always work, because everyone MUST implement order
myPizza.order();
}

这样您使用的是多态性,您所关心的只是对象对order()做出响应。

Pizza示例很糟糕,因为您应该使用一个处理排序的抽象类,而pizzas应该重写Pizza类型。

当您有一个共享属性,但是您的类从不同的地方继承,或者当您没有任何可以使用的公共代码时,您可以使用接口。例如,这是可以被释放的东西IDisposable,你知道它会被释放,你只是不知道当它被释放时会发生什么。

接口只是一个契约,它告诉你一个对象可以做一些事情,什么样的参数和期望什么样的返回类型。

在这种情况下,您可以(也可能会)定义一个Pizza基类并从它们继承。然而,接口允许你做一些其他方式无法做到的事情有两个原因:

  1. 一个类可以实现多个接口。它只是定义类必须具有的特性。实现一系列接口意味着一个类可以在不同的地方实现多种功能。

  2. 接口可以定义在比类或调用方更大的范围内。这意味着您可以分离功能,分离项目依赖项,并将功能保留在一个项目或类中,并在其他地方实现该功能。

2的一个含义是,您可以更改正在使用的类,只需要它实现适当的接口。

接口的主要目的是在您和实现该接口的任何其他类之间建立契约,使您的代码解耦并允许可扩展性。

如果我正在使用一个API来绘制形状,我可能想使用DirectX或图形调用,或OpenGL。因此,我将创建一个接口,它将从您调用的内容中抽象出我的实现。

所以你调用一个工厂方法:MyInterface i = MyGraphics.getInstance()。然后,你有一个契约,所以你知道你可以在MyInterface中期望什么函数。因此,你可以调用i.drawRectanglei.drawCube,并知道如果你将一个库替换为另一个库,函数是受支持的。

如果您正在使用依赖注入,这就变得更加重要,因为您可以在XML文件中交换实现。

所以,你可能有一个加密库可以导出,供一般使用,而另一个加密库只出售给美国公司,区别在于你改变了配置文件,而程序的其余部分不会改变。

这在。net中的集合中被大量使用,例如,你应该只使用List变量,不要担心它是数组列表还是链表。

只要您编写了接口代码,那么开发人员就可以更改实际的实现,而程序的其余部分则保持不变。

这在单元测试时也很有用,因为您可以模拟出整个接口,因此,我不需要访问数据库,而是只返回静态数据的模拟出来的实现,因此我可以测试我的方法,而不用担心数据库是否需要维护。

考虑一下不控制或不拥有基类的情况。

以可视化控件为例,在。net for Winforms中,它们都继承自。net框架中完全定义的基类Control。

让我们假设您从事创建自定义控件的业务。你想要建立新的按钮,文本框,列表视图,网格,等等,你希望他们都有特定的功能独特的控件集。

例如,你可能想要一种通用的方法来处理主题,或者一种通用的方法来处理本地化。

在这种情况下,你不能“只创建一个基类”,因为如果你这样做,你必须重新实现与控件相关的一切

相反,您将从按钮,TextBox, ListView, GridView等下降,并添加您的代码。

但这就产生了一个问题,你现在如何识别哪些控件是“你的”,你如何构建一些代码来表明“对于窗体上所有属于我的控件,将主题设置为X”。

输入接口。

接口是一种查看对象、确定对象是否遵守某种约定的方法。

您可以创建“YourButton”,从Button向下延伸,并添加对所需的所有接口的支持。

这将允许您编写如下代码:

foreach (Control ctrl in Controls)
{
if (ctrl is IMyThemableControl)
((IMyThemableControl)ctrl).SetTheme(newTheme);
}

如果没有接口,这是不可能的,相反,你必须写这样的代码:

foreach (Control ctrl in Controls)
{
if (ctrl is MyThemableButton)
((MyThemableButton)ctrl).SetTheme(newTheme);
else if (ctrl is MyThemableTextBox)
((MyThemableTextBox)ctrl).SetTheme(newTheme);
else if (ctrl is MyThemableGridView)
((MyThemableGridView)ctrl).SetTheme(newTheme);
else ....
}

这些都是很好的例子。

另外,在switch语句的情况下,您不再需要在每次希望里约热内卢以特定方式执行任务时进行维护和切换。

在你的披萨例子中,如果你想做一个披萨,界面就是你所需要的,从那里每个披萨都有自己的逻辑。

这有助于减少耦合和圈复杂度。您仍然需要实现逻辑,但在更广泛的情况下,您需要跟踪的内容将更少。

对于每个披萨,您可以跟踪特定于该披萨的信息。其他披萨有什么并不重要,因为只有其他披萨需要知道。

接口用于应用不同类之间的连接。例如,你有一个关于汽车和树的类;

public class Car { ... }


public class Tree { ... }

您希望为这两个类添加一个可燃功能。但是每个职业都有自己的燃烧方式。所以你只要做;

public class Car : IBurnable
{
public void Burn() { ... }
}


public class Tree : IBurnable
{
public void Burn() { ... }
}

考虑接口的最简单方法是认识继承的意义。如果类CC继承了类C,这意味着:

    类CC可以使用类C的任何public或protected成员,就像它们是自己的一样,因此只需要实现父类中不存在的东西。 对CC的引用可以传递或赋值给需要对C引用的例程或变量。

遗传的这两个功能在某种意义上是相互独立的;虽然继承同时应用这两个,但也可以应用第二个而不应用第一个。这很有用,因为允许一个对象从两个或多个不相关的类继承成员要比允许一种类型可以替代多种类型复杂得多。

接口有点像抽象基类,但有一个关键的区别:继承基类的对象不能继承任何其他类。相反,一个对象可以实现一个接口,而不影响它继承任何所需类或实现任何其他接口的能力。

它的一个很好的特性(恕我直言,在.net框架中未被充分利用)是可以声明性地指示对象可以做的事情。例如,有些对象需要数据源对象,它们可以通过索引从其中检索内容(就像使用List一样),但它们不需要在其中存储任何内容。其他例程将需要一个数据存储对象,在其中可以不按索引存储内容(如Collection.Add),但它们不需要读回任何内容。一些数据类型允许通过索引访问,但不允许写入;另一些允许写入,但不允许通过索引访问。当然,有些国家两者都允许。

如果ReadableByIndex和Appendable是不相关的基类,就不可能定义一个既可以传递给需要ReadableByIndex的对象又可以传递给需要Appendable的对象的类型。我们可以尝试通过从ReadableByIndex或Appendable派生另一个来缓解这个问题;派生类必须为这两个目的提供可用的公共成员,但警告一些公共成员实际上可能不起作用。微软的一些类和接口可以做到这一点,但这相当繁琐。一种更简洁的方法是为不同的目的提供接口,然后让对象为它们实际可以做的事情实现接口。如果有一个接口IReadableByIndex和另一个接口IAppendable,可以做其中一个的类就可以为它们能做的事情实现适当的接口。

你会得到界面,当你需要他们:)你可以研究例子,但你需要的是啊哈!效果才能真正得到他们。

现在您已经知道了接口是什么,只需编写没有接口的代码。您迟早会遇到一个问题,在这个问题中使用接口将是最自然的事情。

没有人真正清楚地解释过接口是如何有用的,所以我打算尝试一下(并从Shamim的回答中窃取一点想法)。

让我们以披萨订购服务为例。您可以有多种类型的披萨,每个披萨的共同操作是在系统中准备订单。每个披萨必须要做好准备,但每个披萨准备方式不同。例如,当点菜时,系统可能需要验证餐厅是否有特定的食材,并将不需要做深盘披萨的食材放在一边。

当用代码写这个的时候,技术上你可以这样做

public class Pizza
{
public void Prepare(PizzaType tp)
{
switch (tp)
{
case PizzaType.StuffedCrust:
// prepare stuffed crust ingredients in system
break;
                

case PizzaType.DeepDish:
// prepare deep dish ingredients in system
break;
                

//.... etc.
}
}
}

然而,深盘披萨(在c#术语中)可能需要在Prepare()方法中设置不同于填充皮的属性,因此你最终会有很多可选属性,并且类不能很好地伸缩(如果你添加新的披萨类型会怎样)。

解决这个问题的正确方法是使用接口。界面声明所有披萨都可以准备,但每个披萨可以准备不同。如果你有以下接口:

public interface IPizza
{
void Prepare();
}


public class StuffedCrustPizza : IPizza
{
public void Prepare()
{
// Set settings in system for stuffed crust preparations
}
}


public class DeepDishPizza : IPizza
{
public void Prepare()
{
// Set settings in system for deep dish preparations
}
}

现在,您的订单处理代码不需要确切地知道订购了什么类型的披萨来处理配料。它只有:

public PreparePizzas(IList<IPizza> pizzas)
{
foreach (IPizza pizza in pizzas)
pizza.Prepare();
}

尽管每种类型的披萨都是不同的,但这部分代码并不需要关心我们处理的是哪种类型的披萨,它只知道正在为披萨调用它,因此每次调用Prepare都会根据其类型自动正确地准备每个披萨,即使集合有多种类型的披萨。

我很惊讶,没有多少帖子包含一个最重要的接口:设计模式。这是使用契约的更大的前景,尽管它是机器代码的语法装饰(老实说,编译器可能会忽略它们),但抽象和接口对于OOP、人类理解和复杂的系统架构来说是至关重要的。

让我们把披萨的比喻扩大到一顿完整的大餐。对于所有的食物类别,我们仍然有核心的Prepare()接口,但我们也有对课程选择(前菜、主菜、甜点)的抽象声明,以及食物类型的不同属性(咸的/甜的、素食的/非素食的、无麸质的等)。

基于这些规范,我们可以实现抽象工厂模式来概念化整个过程,但使用接口来确保只有基础是具体的。其他一切都可以变得灵活或鼓励多态性,但在实现ICourse接口的Course的不同类之间保持封装。

如果我有更多的时间,我想画一个完整的例子,或者有人可以为我扩展它,但总的来说,c#接口将是设计这类系统的最佳工具。

对我来说,刚开始的时候,只有当你不再把它们看作是让你的代码更容易/更快编写的东西时,它们的意义才变得清晰——这不是它们的目的。它们有很多用途:

(这里就没有披萨的比喻了,因为这个比喻的用法不太容易想象)

假设你正在屏幕上制作一款简单的游戏,游戏中会有与你互动的生物。

答:通过在前端和后端实现之间引入松散耦合,它们可以使您的代码在将来更容易维护。

你可以这样写,因为这里只会有喷子:

// This is our back-end implementation of a troll
class Troll
{
void Walk(int distance)
{
//Implementation here
}
}

前端:

function SpawnCreature()
{
Troll aTroll = new Troll();
    

aTroll.Walk(1);
}

两周后,市场营销决定你也需要半兽人,因为他们在twitter上看到了他们,所以你必须做如下事情:

class Orc
{
void Walk(int distance)
{
//Implementation (orcs are faster than trolls)
}
}

前端:

void SpawnCreature(creatureType)
{
switch(creatureType)
{
case Orc:


Orc anOrc = new Orc();
anORc.Walk();


case Troll:


Troll aTroll = new Troll();
aTroll.Walk();
}
}

你可以看到这是如何变得混乱的。你可以在这里使用一个接口,这样你的前端就会被编写一次(这里是重要的部分)测试,然后你可以根据需要插入更多的后端项目:

interface ICreature
{
void Walk(int distance)
}


public class Troll : ICreature
public class Orc : ICreature


//etc

前端则为:

void SpawnCreature(creatureType)
{
ICreature creature;


switch(creatureType)
{
case Orc:


creature = new Orc();


case Troll:


creature = new Troll();
}


creature.Walk();
}

前端现在只关心接口ICreature -它不关心喷子或兽人的内部实现,而只关心他们实现ICreature的事实。

当从这个角度来看这个问题时,需要注意的一点是,你也可以很容易地使用抽象生物类,从这个角度来看,这具有相同效果。

你可以将创建的内容提取到工厂:

public class CreatureFactory {


public ICreature GetCreature(creatureType)
{
ICreature creature;


switch(creatureType)
{
case Orc:


creature = new Orc();


case Troll:


creature = new Troll();
}


return creature;
}
}

我们的前端会变成:

CreatureFactory _factory;


void SpawnCreature(creatureType)
{
ICreature creature = _factory.GetCreature(creatureType);


creature.Walk();
}

现在,前端甚至不需要有实现Troll和Orc的库的引用(假设工厂在一个单独的库中)——它不需要知道任何关于它们的信息。

B:假设你拥有在你的同质数据结构中只有某些生物才拥有的功能

interface ICanTurnToStone
{
void TurnToStone();
}


public class Troll: ICreature, ICanTurnToStone

前端可以是:

void SpawnCreatureInSunlight(creatureType)
{
ICreature creature = _factory.GetCreature(creatureType);


creature.Walk();


if (creature is ICanTurnToStone)
{
(ICanTurnToStone)creature.TurnToStone();
}
}

C:依赖注入用法

大多数依赖注入框架在前端代码和后端实现之间存在非常松散的耦合时才能工作。如果我们以上面的工厂为例,让我们的工厂实现一个接口:

public interface ICreatureFactory {
ICreature GetCreature(string creatureType);
}

我们的前端可以通过构造函数注入(例如MVC API控制器)(通常):

public class CreatureController : Controller {


private readonly ICreatureFactory _factory;


public CreatureController(ICreatureFactory factory) {
_factory = factory;
}


public HttpResponseMessage TurnToStone(string creatureType) {


ICreature creature = _factory.GetCreature(creatureType);
   

creature.TurnToStone();


return Request.CreateResponse(HttpStatusCode.OK);
}
}

使用我们的DI框架(例如Ninject或Autofac),我们可以设置它们,以便在运行时在构造函数中需要ICreatureFactory时创建一个CreatureFactory实例——这使我们的代码美观而简单。

这也意味着当我们为控制器编写单元测试时,我们可以提供一个模拟的ICreatureFactory(例如,如果具体实现需要访问DB,我们不希望我们的单元测试依赖于它),并轻松地测试控制器中的代码。

D:还有其他用途,例如,你有两个项目A和B,由于“遗留”原因,它们没有很好地组织起来,而A有B的参考。

然后在B中发现需要调用a中已经存在的方法的功能。由于您获得的是循环引用,因此不能使用具体实现来实现。

你可以在B中声明一个接口,然后由A中的类实现。你在B中的方法可以被传递一个实现接口的类的实例,即使具体对象是a中的类型。

我同意你的观点,接口是不必要的。这里引用Cwalina pg 80框架设计指南中的一句话:“我经常听到人们说接口指定契约。我认为这是一个危险的神话。接口本身没有指定太多. ...”他和合著者Abrams为微软管理了3个。net版本。他接着说,“契约”是在类的实现中“表示”的。在我看来,几十年来,有很多人警告微软,在OLE/COM中最大限度地发挥工程范例可能看起来很好,但它对硬件的用处更直接。尤其是在80年代和90年代把互操作标准编纂成法典。在我们的TCP/IP互联网世界中,我们很少欣赏硬件和软件体操,我们会跳过在大型机、小型机和微处理器之间“连接”解决方案,而pc只是其中的一小部分。因此,对接口及其协议进行编码使计算工作得以进行。界面主宰了一切。但是解决X.25在应用程序中正常工作的问题与发布假日食谱有什么共同之处呢?我已经编写c++和c#很多年了,但我从来没有创建过一个。

接口也可以通过菊花链来创建另一个接口。这种实现多个接口的能力使开发人员可以在不改变当前类功能的情况下向类中添加功能(SOLID原则)

O = "类应该对扩展开放,对修改关闭"

下面是一个矩形对象的接口:

interface IRectangular
{
Int32 Width();
Int32 Height();
}

它所要求的是实现访问对象宽度和高度的方法。

现在让我们定义一个方法,它可以作用于任何IRectangular类型的对象:

static class Utils
{
public static Int32 Area(IRectangular rect)
{
return rect.Width() * rect.Height();
}
}

这将返回任意矩形物体的面积。

让我们实现一个矩形类SwimmingPool:

class SwimmingPool : IRectangular
{
int width;
int height;


public SwimmingPool(int w, int h)
{ width = w; height = h; }


public int Width() { return width; }
public int Height() { return height; }
}

另一个类House也是矩形的:

class House : IRectangular
{
int width;
int height;


public House(int w, int h)
{ width = w; height = h; }


public int Width() { return width; }
public int Height() { return height; }
}

鉴于此,你可以在房屋或游泳池上调用Area方法:

var house = new House(2, 3);


var pool = new SwimmingPool(3, 4);


Console.WriteLine(Utils.Area(house));
Console.WriteLine(Utils.Area(pool));

通过这种方式,您的类可以从任意数量的接口“继承”行为(静态方法)。

上面的例子没有多大意义。你可以使用类(抽象类,如果你想让它只表现为合同)来完成以上所有的例子:

public abstract class Food {
public abstract void Prepare();
}


public class Pizza : Food  {
public override void Prepare() { /* Prepare pizza */ }
}


public class Burger : Food  {
public override void Prepare() { /* Prepare Burger */ }
}

你会得到和界面相同的行为。你可以创建一个List<Food>并迭代它,w/o知道什么类在上面。

更合适的例子是多重继承:

public abstract class MenuItem {
public string Name { get; set; }
public abstract void BringToTable();
}


// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
public override void BringToTable() { /* Bring soda to table */ }
}




// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
void Cook();
}


public class Pizza : MenuItem, IFood {
public override void BringToTable() { /* Bring pizza to table */ }
public void Cook() { /* Cook Pizza */ }
}


public class Burger : MenuItem, IFood {
public override void BringToTable() { /* Bring burger to table */ }
public void Cook() { /* Cook Burger */ }
}

然后你可以将它们全部用作MenuItem,而不关心它们如何处理每个方法调用。

public class Waiter {
public void TakeOrder(IEnumerable<MenuItem> order)
{
// Cook first
// (all except soda because soda is not IFood)
foreach (var food in order.OfType<IFood>())
food.Cook();


// Bring them all to the table
// (everything, including soda, pizza and burger because they're all menu items)
foreach (var menuItem in order)
menuItem.BringToTable();
}
}

我在这个页面上搜索了“合成”这个词,但一次也没看到。这个答案是对前面提到的答案的补充。

在面向对象项目中使用接口的一个绝对重要的原因是,它们允许你更倾向于组合而不是继承。通过实现接口,您可以将您的实现与应用于它们的各种算法解耦。

德里克·巴纳斯(Derek Banas)的“装饰图案”教程(有趣的是,它也以披萨为例)是一个有价值的插图:

https://www.youtube.com/watch?v=j40kRwSm4VE

这里有很多很好的答案,但我想从一个稍微不同的角度来尝试。

你可能熟悉面向对象设计的SOLID原则。总而言之:

S -单一责任原则 O -开/闭原则 利斯科夫替换原理 I -界面隔离原理 D -依赖倒置原理

遵循SOLID原则有助于生成干净、分解良好、内聚和松散耦合的代码。考虑到:

用法与例句:“依赖管理是软件在任何规模上的主要挑战”(唐纳德·克努特)

那么任何有助于依赖管理的东西都是一个巨大的胜利。接口和依赖倒置原则确实有助于将代码与具体类的依赖解耦,因此可以根据行为而不是实现来编写和推理代码。这有助于将代码分解成可以在运行时而不是编译时组合的组件,也意味着这些组件可以很容易地插入和取出,而无需更改其余代码。

接口尤其有助于依赖倒置原则,其中代码可以组件化为服务集合,每个服务由一个接口描述。然后,通过将服务作为构造函数参数传入,可以在运行时将它们“注入”到类中。如果您开始编写单元测试并使用测试驱动开发,这种技术就变得非常重要。试一试!您将很快了解接口如何帮助将代码分解为可管理的块,以便单独进行测试。

类比简单解释

无接口(例1):

示例1:without an interface .

无接口(例2):

示例2:without an interface

有接口:

Example 3: The benefits of Using an Interface .

需要解决的问题:多态性的目的是什么?

比方说,我是一个建筑工地的领班。我不知道哪个商人会走进来。但我告诉他们该怎么做。

  1. 如果是木匠,我说:build wooden scaffolding
  2. 如果是管道工,我会说:Set up the pipes
  3. 如果它是一个人民党政府官员,我说,three bags full of cash, sir.

上述方法的问题在于:(1)我必须知道谁会走进那扇门,并且根据是谁,我必须告诉他们该做什么。这通常会使代码更难维护或更容易出错。

知道该做什么的含义:

  • 这意味着如果木匠的代码从:BuildScaffolding()更改为BuildScaffold()(即轻微的名称更改),那么我也将不得不更改调用类(即Foreperson类)以及-你将不得不对代码进行< >强两个< / >强更改,而不是(基本上)只有一个。使用多态性,您(基本上)只需要做一个更改就可以获得相同的结果。

  • 其次,你不必不断地问:你是谁?好吧,这样做……你是谁?好的,这样做.....多态性——它dry代码,在某些情况下非常有效:

  • 使用多态性,您可以轻松地添加额外的贸易人员类,而无需更改任何现有代码。(即SOLID设计原则的第二条:开闭原则)。

解决方案

想象这样一个场景,无论谁走进家门,我都可以说:“工作()”;他们做着自己擅长的工作:管道工处理管道,电工处理电线,而官僚则专门收取贿赂,为其他人做双倍的工作。

这种方法的好处是:(i)我不需要确切地知道谁会走进那扇门——我只需要知道他们是一种手工工人,他们会工作,其次,(ii)我不需要知道任何关于特定行业的信息。手工工人会处理的。

所以不要这样:

if(electrician) then  electrician.FixCablesAndElectricity()


if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks()


if(keralaCustoms) then keralaCustoms.askForBribes()

我可以这样做:

ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment.


tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!!

有什么好处?

这样做的好处是,如果木匠等特定的工作要求发生了变化,那么领班就不需要改变他的代码——他不需要知道或关心。重要的是木匠知道Work()是什么意思。其次,如果一种新型的建筑工人来到工地上,那么工头不需要知道任何关于贸易的事情——工头所关心的是建筑工人(.e。焊工、上釉工、瓦工等)可以完成一些工作。

总结

界面允许您让人完成分配给他们的工作,而不需要您确切地知道他们是谁或他们可以做什么。这允许您轻松地添加新的(交易)类型,而无需更改现有的代码(从技术上讲,您确实更改了一点点),这是面向对象方法相对于更函数式编程方法的真正好处。

如果你不理解上面的任何内容,或者不清楚,请在评论中提问,我会尽量让答案更好。

对我来说,接口的优点是它比抽象类更灵活。由于您只能继承一个抽象类,但可以实现多个接口,因此对在许多地方继承抽象类的系统进行更改就会出现问题。如果在100个地方继承了它,则更改需要对所有100个地方进行更改。但是,通过接口,您可以将新的更改放在一个新接口中,并在需要的地方使用该接口(interface Seq。从固体)。此外,使用接口的内存使用量似乎会更少,因为接口示例中的对象只在内存中使用一次,而不管有多少地方实现了接口。

class Program {
static void Main(string[] args) {
IMachine machine = new Machine();
machine.Run();
Console.ReadKey();
}


}


class Machine : IMachine {
private void Run() {
Console.WriteLine("Running...");
}
void IMachine.Run() => Run();
}


interface IMachine
{
void Run();
}

让我从另一个角度来描述一下。让我们根据上面的例子来创建一个故事;

程序、机器和IMachine是我们故事的演员。程序想要运行,但它没有这个能力,而机器知道如何运行。机器和IMachine是最好的朋友,但程序和机器没有说话的关系。于是程序和IMachine做了一个交易,决定IMachine通过观察机器(就像一个反射器)来告诉程序如何运行。

Program在IMachine的帮助下学习如何运行。

接口提供通信和开发松散耦合的项目。

我把具体类的方法作为私有。我在这里的目标是通过防止访问具体的类属性和方法来实现松耦合,只允许通过接口访问它们。(所以我明确地定义了接口的方法)。

什么?

接口基本上是一个契约,接口的所有类实现都应该遵循。它们看起来像一个类,但没有实现。

C#中,接口名称约定由前缀“I”定义,所以如果你想有一个名为shapes的接口,你可以将其声明为IShapes

为什么?

Improves code re-usability

假设你想要绘制CircleTriangle. 你可以将它们组合在一起并称为__abc0,并且有方法来绘制CircleTriangle 但有具体的实现将是一个坏主意,因为明天你可能会决定有2个Shapes Rectangle &Square。现在,当你添加它们时,很有可能会破坏代码的其他部分

使用Interface,您可以将不同的实现与契约隔离开来


现场场景第一天

你被要求创建一个应用程序来绘制圆形和三角形

interface IShapes
{
void DrawShape();
   

}


class Circle : IShapes
{
    

public void DrawShape()
{
Console.WriteLine("Implementation to Draw a Circle");
}
}


Class Triangle: IShapes
{
public void DrawShape()
{
Console.WriteLine("Implementation to draw a Triangle");
}
}
static void Main()
{
List <IShapes> shapes = new List<IShapes>();
shapes.Add(new Circle());
shapes.Add(new Triangle());


foreach(var shape in shapes)
{
shape.DrawShape();
}
}

现场场景第二天

如果你被要求添加SquareRectangle,你所要做的就是在class Square: IShapes中创建它的实现,在Main中添加到列表shapes.Add(new Square());

接口用于驱动一致性,以一种松散耦合的方式,这使得它不同于紧密耦合的抽象类。这就是为什么它通常也被定义为合同。实现接口的任何类都必须遵守接口定义的“规则/语法”,并且其中没有具体的元素。

我将给出一个下图所支持的例子。

想象在一个工厂里有三种机器。矩形机,三角形机和多边形机。时代竞争激烈,你希望简化操作员培训。你只需要训练他们使用一种启动和停止机器的方法,所以你有一个绿色的启动按钮和红色的停止按钮。所以现在在3个不同的机器上,你有一个一致的方式来启动和停止3个不同类型的机器。现在想象一下这些机器是类类需要有启动和停止方法,你如何在这些不同的类之间保持一致性?界面就是答案。

enter image description here

一个简单的例子来帮助你可视化,有人可能会问为什么不用抽象类呢?有了接口,对象不必直接相关或继承,你仍然可以在不同的类之间保持一致性。

public interface IMachine
{
bool Start();
bool Stop();
}


public class Car : IMachine
{
public bool Start()
{
Console.WriteLine("Car started");
return true;
}


public bool Stop()
{
Console.WriteLine("Car stopped");
return false;
}
}


public class Tank : IMachine
{
public bool Start()
{
Console.WriteLine("Tank started");
return true;
}


public bool Stop()
{
Console.WriteLine("Tank stopped");
return false;
}
}


class Program
{
static void Main(string[] args)
{
var car = new Car();
car.Start();
car.Stop();


var tank = new Tank();
tank.Start();
tank.Stop();


}
}

我知道我已经迟到了。(差不多九年了),但如果有人想要简单的解释,你可以这样说:

简单地说,当你知道一个对象可以做什么,或者我们要在一个对象上实现什么函数时,你就使用接口。使用实例插入、更新和删除。

interface ICRUD{
void InsertData(); // will insert data
void UpdateData(); // will update data
void DeleteData(); // will delete data
}

重要提示:接口总是公共的。

希望这能有所帮助。

这么多答案! 尽我所能。呵呵。< / p >

首先,你可以使用一个具体的基类和派生类。在这种情况下,你必须在基类中为准备方法做一个空的或无用的实现,也使这个方法virtual,然后派生类将为自己重写这个准备方法。在这种情况下,基类中准备的实现是无用的。

你选择使用Interface的原因是因为你必须定义契约,而不是实现

有一个IPizza类型,它提供了准备的功能。这是合同。它是如何准备的是实现,它是不关你的事。这是各种Pizza实现的责任。 这里首选interfaceabstract类而不是具体基类,因为你必须创建一个抽象,即Prepare方法。不能在具体基类中创建抽象方法

现在你可能会说,为什么不用抽象类呢?

因此,当你需要实现100%的抽象时,你需要使用Interface。但是当你需要一些抽象和具体的实现时,使用abstract类。它的意思。

例子:让我们说你所有的披萨都有一个基础,基础准备将是相同的过程。然而,所有披萨的种类和配料都有所不同。在这种情况下,你可以用抽象方法准备和具体方法PreparePizzaBase创建一个抽象类。

public abstract class Pizza{
// concrete method which is common to all pizzas.
public PizzaBase PreparePizzaBase(){
// code for pizza base preparation.
}
public abstract void Prepare();
}


public class DeluxePizza: Pizza{
public void Prepare(){
var base=PreparePizzaBase();
// prepare deluxe pizza on pizza base.
}
}