接口是否与多态性兼容

时间:2011-05-27 03:49:26

标签: c# .net oop interface polymorphism

我遇到了与多态类型(甚至多态接口)交互的接口概念的问题。我正在使用C#进行开发,并希望保持 close 这个定义的答案,尽管我认为这仍然为每个人提供了足够的空间来提出答案。

就像一个例子,假设你想制作一个绘制东西的程序。您可以为Paints定义一个接口,并为绘制的主题定义一个接口。此外,您可以使用更具体的方式绘制一些主题。

interface IPainter {
  void paint(IPaintable paintable);
}
interface IPaintable {
  void ApplyBaseLayer(Color c);
}
interface IDecalPaintable : IPaintable {
  void ApplyDecal(HatchBrush b);
}

我可以想象制作一个类似于以下的画家:

class AwesomeDecalPainter : IPainter
  {
    public void paint(IPaintable paintable) {
      IDecalPaintable decalPaintable = (IDecalPaintable)paintable;
      decalPaintable.ApplyBaseLayer(Color.Red);
      decalPaintable.ApplyDecal(new HatchBrush(HatchStyle.Plaid, Color.Green));
    }
  }

当然,如果paintable没有实现IDecalPaintable,这将抛出。它立即引入了IPainter实现与其操作的IPaintable之间的耦合。但是我也认为说AwesomeDecalPainter不是IPainter是不合理的,因为它的使用仅限于IPaintable域的一个子集。

所以我的问题实际上是四方面的:

  • 界面是否兼容 多态性?
  • 好吗? 设计实现一个IPainter 可以在IDecalPaintable上运行吗?
  • 如果它可以独家运作怎么样? 在IDecalPaintable?
  • 是否有任何文献或源代码 举例说明了接口和多态类型 应该互动?

4 个答案:

答案 0 :(得分:16)

类的接口意味着该类的“用户”的工具。接口是该类的公共表示,它应该向任何考虑使用它的人宣传哪些方法和常量可用并可从外部访问。因此,顾名思义,它始终位于用户和实现它的类之间。

另一方面,抽象类是一种旨在帮助扩展它的类的“实现者”的工具。它是一个基础设施,可以对具体类应该是什么样的实施限制和指导。从类设计的角度来看,抽象类在界面上比界面更重要。在这种情况下,实现者位于抽象类和具体类之间,将后者构建在前者之上。

所以简单地回答你的问题,Interface是一个尊重代码的“契约”。当以这种方式使用时,它更适用于多态性的继承。

抽象类,它们定义'类型'。当具体的子类使用抽象类并重新定义方法时,添加新的等等......你会看到多态现象。

我知道这篇文章可能会让你更加困惑,直到我学会了设计模式才对我有意义。通过一些简单的模式,您将更好地理解每个对象的作用以及继承,多态和封装如何共同构建设计干净的应用程序。

好运

答案 1 :(得分:8)

接口不仅与多态兼容 - 它们也是必不可少的。您似乎缺少的部分想法是,如果有一个像IPaintable这样的接口,期望实现它的每个对象都会提供一些默认的绘制方法,通常封装其他方法,以一些有用的方式配置对象

例如,一个IPaintable接口可能会定义一个paint方法:

void Paint(ICanvas canvas);

请注意,Paint方法没有说明应该绘制什么,也没有说明什么颜色,也没有任何其他内容。各种可绘制的对象将暴露属性以控制这些事物。例如,Polygon方法可能会处理OutlineColor和FillColor属性以及SetVertices()方法。想要绘制大量对象的例程可以接受IEnumerable(Of IPaintable)并简单地在所有对象上调用Paint,而不必知道它们将如何被绘制。通过调用Paint来绘制对象的事实,除了应该绘制它的画布之外没有任何参数,绝不会阻止Paint方法执行各种奇特的渐变填充,纹理映射,光线跟踪或其他图形效果。指挥绘画的代码对这些东西一无所知,但是IPaintable对象本身会保存他们需要的所有信息,因此不需要调用代码来提供它。

答案 2 :(得分:5)

问题实际上是你定义了一个模糊的界面,合同是一个更合适的术语。这就像你进入麦当劳并且只是点一个汉堡:

interface Resturant
{
    Burger BuyBurger();
}

接受订单的人会在一段时间内看起来有点困惑,但最终他/她会为你提供任何汉堡,因为你没有说明你想要的东西。

这里也是一样的。你真的只想定义一些东西是可以涂漆的吗?如果你问我,就像在寻找麻烦一样。始终尝试使接口尽可能具体。拥有几个小的特定接口总是比一般的大接口更好。

但让我们回到你的榜样。在您的情况下,所有类只需要能够绘制一些东西。因此,我会添加另一个更具体的界面:

    interface IPainter
    {
        void Paint(IPaintable paintable);
    }
    interface IDecalPainter : IPainter
    {
        void Paint(IDecalPaintable paintable);
    }
    interface IPaintable
    {
        void ApplyBaseLayer(Color c);
    }
    interface IDecalPaintable : IPaintable
    {
        void ApplyDecal(HatchBrush b);
    }

    class AwesomeDecalPainter : IDecalPainter
    {
        public void Paint(IPaintable paintable)
        {
            IDecalPaintable decalPaintable = paintable as IDecalPaintable;
            if (decalPaintable != null)
                Paint(decalPaintable);
            else
                paintable.ApplyBaseLayer(Color.Red);
        }

        public void Paint(IDecalPaintable paintable)
        {
            paintable.ApplyBaseLayer(Color.Red);
            paintable.ApplyDecal(new HatchBrush(HatchStyle.Plaid, Color.Green));
        }
    }

我建议您阅读有关SOLID原则的内容。

更新

更健全的界面实现

    interface IPaintingContext
    {
        //Should not be object. But my System.Drawing knowledge is limited
        object DrawingSurface { get; set; }
    }
    interface IPaintable
    {
        void Draw(IPaintingContext context);
    }

    class AwesomeDecal : IPaintable
    {
        public void Draw(IPaintingContext paintable)
        {
            // draw decal
        }
    }

    class ComplexPaintableObject : IPaintable
    {
        public ComplexPaintableObject(IEnumerable<IPaintable> paintable)
        {
            // add background, border 
        }
    }

在这里,您可以创建尽可能复杂的绘画项目。他们现在知道他们画的是什么,或者在同一表面上使用了什么其他的痛苦表。

答案 3 :(得分:0)

我能正确理解你的问题吗?

我认为你的问题是界面是否可以运用继承的基本要素?

(即从父接口继承的子接口)。

1)如果是这种情况,从语法上讲,为什么不呢?

2)至于实用性,它可能没什么价值,因为你的子接口基本上没有从你的父接口重用任何东西。