Совместимы ли интерфейсы с полиморфизмом - PullRequest
21 голосов
/ 27 мая 2011

У меня возникли проблемы с концепцией интерфейсов, взаимодействующих с полиморфными типами (или даже полиморфными интерфейсами). Я занимаюсь разработкой на 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));
    }
  }

Конечно, это будет сгенерировано, если painttable не реализует IDecalPaintable. Он сразу вводит связь между реализацией IPainter и IPaintable, на котором он работает. Однако я также не думаю, что имеет смысл говорить, что AwesomeDecalPainter не является IPainter только потому, что его использование ограничено подмножеством домена IPaintable.

Так что мой вопрос действительно четырехкратный:

  • Интерфейс совместим с полиморфизм вообще?
  • Это хорошо? дизайн для реализации IPainter, который может работать на IDecalPaintable?
  • А если он может работать исключительно на IDecalPaintable?
  • Есть ли литература или исходный код, который иллюстрирует, как интерфейсы и полиморфные типы должен взаимодействовать?

Ответы [ 4 ]

16 голосов
/ 27 мая 2011

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

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

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

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

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

Удачи

8 голосов
/ 27 мая 2011

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

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

void Paint(ICanvas canvas);

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

5 голосов
/ 27 мая 2011

Проблема действительно в том, что вы определили неопределенный интерфейс, контракт - более подходящий термин.Как будто вы заходите в 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.

Обновление

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

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

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

0 голосов
/ 27 мая 2011

Я правильно понимаю ваш вопрос?

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

(т.е. дочерний интерфейс наследуется от родительского интерфейса).

1) если это так, то синтаксически уверен, почему бы и нет?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...