Подтвердить что ты не робот

Совместимы ли интерфейсы с полиморфизмом

У меня возникают проблемы с концепцией взаимодействия интерфейсов с полиморфными типами (или даже с полиморфными интерфейсами). Я развиваюсь на С# и буду благодарен за ответы, стоящие рядом с этим определением, хотя я думаю, что все еще дает достаточно места, чтобы каждый мог ответить.

Как пример, скажем, вы хотите сделать программу для рисования вещей. Вы определяете интерфейс для актера, который является Красками, и вы определяете интерфейс для объекта, который окрашен. Кроме того, у вас есть некоторые предметы, которые можно раскрасить более определенным образом.

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 не является IP-узлом только потому, что он используется только подмножеством домена IPaintable.

Итак, мой вопрос действительно четыре раза:

  • Совместимы ли интерфейсы с полиморфизм вообще?
  • Это хорошо дизайн для внедрения IP-интерфейса, который может работать на IDecalPaintable?
  • Как насчет того, может ли он работать исключительно на IDecalPaintable?
  • Есть ли какая-либо литература или исходный код, который иллюстрирует, как интерфейсы и полиморфные типы должны взаимодействовать?
4b9b3361

Ответ 1

Интерфейс класса подразумевается как инструмент для "пользователя" этого класса. Интерфейс представляет собой публичную презентацию для класса, и он должен рекламировать любого, кто хочет использовать его, какие методы и константы доступны и доступны извне. Поэтому, как следует из названия, он всегда находится между пользователем и классом, реализующим его.

С другой стороны, абстрактный класс является инструментом, помогающим "разработчику" классов, которые его расширяют. Это инфраструктура, которая может налагать ограничения и рекомендации относительно того, как должны выглядеть конкретные классы. С точки зрения дизайна класса абстрактные классы более архитектурно важны, чем интерфейсы. В этом случае разработчик сидит между абстрактным классом и конкретным, создавая последний поверх первого.

Итак, чтобы ответить на ваш вопрос просто, интерфейс - это "контракт", чтобы код уважал. Когда этот метод используется, он больше применим к наследованию, чем полиморфизм.

Абстрактные классы, они определяют "тип". И когда конкретные подклассы используют абстрактные классы и переопределяют методы, добавляют новые и т.д.... там вы видите полиморфизм в действии.

Я знаю, что этот пост может смутить вас больше, это не имело смысла для меня, пока я не изучил шаблоны дизайна. С помощью нескольких простых шаблонов под вашим поясом вы лучше поймете роль каждого объекта и то, как наследование, полиморфизм и инкапсуляция играют рука об руку, чтобы создавать чисто разработанные приложения.

Удача

Ответ 2

Интерфейсы не только совместимы с полиморфизмом - они важны для него. Часть идеи, которую вы, кажется, не замечаете, заключается в том, что если у вас есть интерфейс, такой как IPaintable, ожидание состоит в том, что каждый объект, который его реализует, предоставит некоторый метод по умолчанию для рисования, обычно инкапсулируя другие методы, которые могли бы настроить объект в некотором полезном виде.

Например, интерфейс IPaintable может определить метод рисования:

void Paint(ICanvas canvas);

Обратите внимание, что метод Paint ничего не говорит о том, что должно быть нарисовано, ни о каком цвете, ни о чем другом. Различные объекты с рисунком могут выставлять свойства для управления такими вещами. Например, метод Polygon может располагать свойствами OutlineColor и FillColor вместе с методом SetVertices(). Подпрограмма, которая хочет рисовать множество объектов, может принять IEnumerable (Of IPaintable) и просто называть Paint на всех из них, не зная ничего о том, как они будут нарисованы. Тот факт, что объект окрашен вызовом Paint без каких-либо параметров, отличных от холста, на котором он должен быть окрашен, никоим образом не препятствует тому, чтобы метод Paint выполнял всевозможные заливки градиента, отображение текстур, трассировку лучей или другие графические эффекты. Код, который управляет картиной, ничего не знает о таких вещах, но сами объекты IPaintable будут хранить всю необходимую им информацию и, следовательно, не нуждаются в вызывающем коде для ее доставки.

Ответ 3

Проблема в том, что вы определили неопределенный интерфейс, контракт более правильный. Это как вы идете в McDonalds и просто заказываете гамбургер:

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.

Update

Более реалистичная реализация интерфейса

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

Здесь вы можете создать как можно более сложные элементы рисования. Теперь они знают, что они нарисовали, или какие другие лакокрасочные материалы используются на одной и той же поверхности.

Ответ 4

Я правильно понимаю ваш вопрос? Я думаю, что ваш вопрос заключается в том, может ли интерфейс реализовать основные наследования? (т.е. дочерний интерфейс, наследующий от родительского интерфейса).

1) если это случай, синтаксически уверен, почему бы и нет? 2) что касается практичности, это может иметь мало значения, потому что ваши дочерние интерфейсы практически не используют что-либо из вашего родительского интерфейса.