为了账号安全,请及时绑定邮箱和手机立即绑定

接口-有什么意义?

/ 猿问

接口-有什么意义?

慕侠2389804 2019-10-23 12:51:15

接口的原因确实使我难以理解。据我了解,这是针对C#中不存在的不存在的多重继承的一种解决方法(或者有人告诉我)。


我所看到的是,您预先定义了一些成员和函数,然后必须在类中重新定义它们。从而使接口冗余。只是感觉像语法...好吧,对我来说是垃圾(请不要冒犯。


在下面给出的示例中,该示例是在堆栈溢出时从不同的C#接口线程获取的,我只是创建一个称为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

    }

}


查看完整描述

3 回答

?
慕工程0101907

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

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

Python不是静态类型的,因此类型会在运行时保留并查找。因此,您可以尝试Order()在任何对象上调用方法。只要对象具有这样的方法,运行时就很高兴,并且可能只是耸耸肩说“ Meh。”(如果没有)。在C#中不是这样。编译器负责进行正确的调用,如果只是随机的,object则编译器尚不知道运行时实例是否具有该方法。从编译器的角度来看,它是无效的,因为它无法验证它。(您可以使用反射或dynamic关键字来执行此类操作,但是我猜这有点远了。)

还要注意,通常意义上的接口不一定必须是C#interface,它也可以是抽象类甚至是普通类(如果所有子类都需要共享一些通用代码,则可以派上用场)但是interface就足够了)。


查看完整回答
反对 回复 2019-10-23
?
湖上湖

没有人真正地用简单的术语解释过接口是如何有用的,所以我要给它做个尝试(并从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都会自动正确地准备每个比萨饼基于其类型,即使馆藏有多种比萨饼也是如此。


查看完整回答
反对 回复 2019-10-23
?
牧羊人nacy

对我来说,只有当您停止将它们视为使代码更容易/更快地编写的事情时,这些问题的意义才变得清楚-这不是它们的目的。它们有许多用途:


(这将失去比萨饼的类比,因为很难直观地看到它的用法)


假设您正在屏幕上制作一个简单的游戏,它将有您与之互动的生物。


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


您可以首先编写此代码,因为只会有一些巨魔:


// 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;


    switch(creatureType)

    {

         case Orc:


           creature = new Orc();


          case Troll:


            creature = new Troll();

    }


    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(例如,如果具体实现需要数据库访问权限,我们不希望单元测试依赖于此),并且可以轻松地在控制器中测试代码。


D:还有其他用途,例如,您有两个项目A和B,出于“传统”原因,它们的结构不完善,而A则引用了B。


然后,您可以在B中找到需要调用A中已有方法的功能。当您获得循环引用时,就不能使用具体的实现来实现。


您可以在B中声明一个接口,然后由A中的类实现。即使具体对象是A中的类型,也可以向B中的方法传递实现该接口的类的实例,而不会出现问题。


查看完整回答
反对 回复 2019-10-23

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信