.net 接口是否与多态性兼容

klh5stk1  于 2023-11-20  发布在  .NET
关注(0)|答案(4)|浏览(151)

我在接口与多态类型(甚至多态接口)交互的概念上遇到了麻烦。我正在用C#开发,并希望答案保持接近这个定义,尽管我认为这仍然为每个人提供了足够的空间来提出答案。
举个例子,假设你想写一个程序来画图,你为画图的演员定义了一个接口,你为被画的对象定义了一个接口,此外,你还有一些对象,可以用更具体的方式来画。

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的使用仅限于IPaintable域的子集就说它不是IPainter是有意义的。
所以我的问题实际上是四重的:

  • 接口是否与多态性兼容?
  • 实现一个可以在IDecalPaintable上操作的IPainter是一个好的设计吗?
  • 如果它只能在IDecalPaintable上运行呢?
  • 是否有任何文献或源代码说明接口和多态类型应该如何交互?
dvtswwa3

dvtswwa31#

类的接口是类的“用户”的工具。接口是类的公共表示,它应该向任何考虑使用它的人宣传哪些方法和常量是可用的,可以从外部访问。因此,正如它的名字所暗示的,它总是位于用户和实现它的类之间。
另一方面,抽象类是一种工具,旨在帮助扩展它的类的“实现者”。它是一种基础设施,可以对具体类的外观施加限制和指导。从类设计的Angular 来看,抽象类在架构上比接口更重要。在这种情况下,“实现者”位于抽象类和具体类之间,将后者建立在前者之上。
所以简单地回答你的问题,接口是代码要遵守的一个“契约”。当这样使用时,它更适用于继承而不是多态。
抽象类定义了一个“类型”,而当具体子类使用抽象类并重新定义方法时,添加新的方法等等……在这里你看到了多态性的作用。
我知道这篇文章可能会让你更困惑,直到我学习了设计模式,它才对我有意义。在你的腰带下有一些简单的模式,你会更好地理解每个对象的作用,以及继承,多态性和封装如何携手构建干净设计的应用程序。
好运

nbysray5

nbysray52#

接口不仅与多态性兼容--它们对多态性来说是必不可少的。你似乎忽略了一个想法,即如果有一个像IPaintable这样的接口,那么期望实现它的每个对象都将提供一些被绘制的默认方法,通常封装其他方法,这些方法将以某种有用的方式配置对象。
例如,一个IPaintable接口可以定义一个paint方法:

void Paint(ICanvas canvas);

字符串
请注意,Paint方法没有说明应该绘制什么,什么颜色,也没有说明其他任何事情。各种可绘制的对象将公开属性来控制这些事情。例如,Polygon方法可能会使用SetVertices()方法处理OutlineColor和FillColor属性沿着。想要绘制大量对象的例程可以接受IEColor(Of IPaintable)并简单地对所有这些对象调用Paint,而不必知道它们将如何绘制。通过调用Paint绘制对象,除了绘制对象的画布之外,没有其他参数,这一事实并不妨碍Paint方法进行各种花哨的渐变填充,纹理Map、光线跟踪或其他图形效果。命令绘画的代码对这些东西一无所知,但IPaintable对象本身将保存它们所需的所有信息,因此不需要调用代码来提供这些信息。

2wnc66cl

2wnc66cl3#

问题是你定义了一个模糊的接口,合同是一个更合适的术语。这就像你走进麦当劳,只点了一个汉堡:

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));
        }
    }


我建议你读一下固体原则。

更新

更完善的接口实现

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 
        }
    }


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

ryevplcw

ryevplcw4#

我理解你的问题正确吗?
我想你的问题是接口是否可以行使继承的要领?
(ie.从父接口继承的子接口)。
1)如果是这样的话,从语法上讲,为什么不呢?
2)至于实用性,它可能没有什么价值,因为你的子接口本质上并没有重用父接口的任何东西。

相关问题