Отношение реализации интерфейса C # - это просто отношение "Can-Do"? - PullRequest
9 голосов
/ 01 ноября 2008

Сегодня кто-то сказал мне, что реализация интерфейса в C # - это просто отношение "Can-Do", а не отношение "Is-A". Это противоречит моей давней вере в ЛСП (принцип замещения Лискова). Я всегда думаю, что все наследство должно означать отношения "Is-A".

Итак, если реализация интерфейса - это просто отношение "Can-Do". Что если есть интерфейс "IHuman" и "IEngineer", а один класс "Programmer" наследуется от "IHuman" и "IEngineer"? Конечно, "Программист" - это "IHuman" и "IEngineer".

Если это просто отношение "Can-Do", значит ли это, что мы не можем ожидать, что поведение экземпляра "Programmer" может отличаться, если рассматривать его как IHuman и IEngineer?

Ответы [ 5 ]

16 голосов
/ 01 ноября 2008

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

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

5 голосов
/ 01 ноября 2008

Я склонен думать об интерфейсах как о договоре поведения. Интерфейсы, такие как IComparable и IEnumerable, являются классическими примерами.

В приведенном вами примере IHuman и IEngineer на самом деле не ведут себя.

4 голосов
/ 08 января 2009

Нашел этот вопрос довольно поздно, но я хотел вмешаться.

Интерфейсы в C # имеют отношение is-a, но не is-an-object. Скорее, это реализация.

Другими словами, для класса Foo, который реализует IBar, следующий тест:

Foo myFoo = new Foo(); 
return myFoo is IBar;

буквально возвращает истину. Вы также можете сказать,

IBar bar = myArrayList[3] as IBar;
Foo foo = bar as Foo;

Или, если метод требует IBar, вы можете передать Foo.

void DoSomething(IBar bar) {
}

static void Main() {
    Foo myFoo = new Foo();
    DoSomething(myFoo);
}

Очевидно, что IBar сам по себе не имеет реализации, поэтому отношение can-do также применимо.

interface IBar { void happy(); }
class Foo : IBar
{
    void happy()
    {
        Console.Write("OH MAN I AM SO HAPPY!");
    }
}
class Program
{
    static void Main()
    {
        IBar myBar = new Foo();
        myBar.happy();
    }
}

Но в этом отношении то же самое относится и к наследованию объектов; объект класса Foo, который наследует класс Bar, имеет отношение can-do, а также отношение is-a, так же, как интерфейс. Просто его реализация была заранее построена для него.

Реальный вопрос в том, является ли ... 1019 * что , может ли - что ? Унаследованный объект класса is-a- [родительский экземпляр] и can-do- [поведение родителя] , тогда как реализация интерфейса is-an- [реализация интерфейса ] и, следовательно, can-do- [поведение интерфейса] .

В большинстве случаев, когда используются программные отношения is-a, такие как перечисленные выше, оценивается только интерфейс, поэтому и унаследованный класс, и реализованный интерфейс имеют одинаковые качества is-a и can-do.

НТН, Jon

2 голосов
/ 01 ноября 2008

На самом деле именно поэтому большинство интерфейсов являются возможностями, а не именами, поэтому у вас есть

IComparable, ITestable, IEnumerable

и

человек, животное, собака и т. Д.

В любом случае, как уже упоминалось, вы должны быть прагматичными, у меня есть общее правило, когда я кодирую: никогда не позволяйте понятиям, соглашениям или стандартным практикам встать на пути выполнения работы Лучше быть прагматичным, чем академическим.

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

2 голосов
/ 01 ноября 2008

Разработчики .NET Framework используют интерфейсы для обозначения отношения «имеет» (или «может сделать»), тогда как «is a» реализуется с использованием наследования.

Обоснование этого можно найти в разделе Выбор между классами и интерфейсами Руководства разработчика .NET Framework:

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

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

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