Наследование интерфейса и наследование реализации

Наследование реализации;

Наследование

В объектно-ориентированном программировании (ООП) существуют два различных типа наследования: наследование реализации и наследование интерфейса.

1. Наследование реализации (implementation inheritance) означает, что тип происходит от базового типа, получая от него все поля-члены и функции-члены. При наследовании реализации производный тип адаптирует реализацию каждой функции базового типа, если только в его определении не указано, что реализация функции должна быть переопределена. Такой тип наследования более полезен, когда нужно добавить функциональность к существующему типу или же когда несколько связанных типов разделяют существенный объем общей функциональности.

2. Наследование интерфейса (interface inheritance) означает, что тип наследует только сигнатуру функций, но не наследует никакой реализации. Этот тип наследования наиболее полезен, когда нужно специфицировать, что тип обеспечивает доступ к определенным средствам.

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

Некоторые языки, такие как C++, поддерживают то, что известно под названием множественного наследования, когда класс происходит более чем от одного базового класса. Преимущества множественного наследования спорны. С одной стороны, нет сомнений, что можно применять множественное наследование для написания чрезвычайно сложного, но при этом компактного кода, что демонстрирует библиотека C++ ATL. С другой стороны, код, использующий множественное наследование, часто трудно понять и сложно отлаживать (все это также демонстрирует библиотека C++ ATL). Как уже упоминалось, облегчение написания устойчивого кода было одной из ключевых целей проектирования С#. Соответственно, поэтому в С# множественное наследование не поддерживается. Однако С# позволяет типу наследовать множество интерфейсов. Это значит, что класс С# может наследоваться от другого класса и любого количества интерфейсов. На самом деле можно сказать точнее: благодаря наличию System.Object как всеобщего базового типа, каждый класс С# (за исключением Object) имеет строго один базовый класс и дополнительно может иметь любое количество базовых интерфейсов.

Для объявления, что класс наследуется от другого класса, применяется следующий синтаксис:

class УнаследованныйКласс: БазовыйКласс< //Данные-члены и функции-члены>

Этот синтаксис очень похож на синтаксис C++ и Java. Однако программисты на C++, знакомые с концепцией общедоступного и приватного наследования, должны обратить внимание, что С# не поддерживает приватного наследования; этим объясняется отсутствие квалификатора public или private перед именем базового класса. Поддержка приватного наследования значительно усложняет язык, при этом принося весьма небольшую выгоду. На практике приватное наследование в C++ все равно используется чрезвычайно редко. Если класс также наследует интерфейсы, то список базового класса и интерфейсов

public class MyDerivedClass: MyBaseClass, Ilnterfacel, IInterface2< // и т.д.>

Для структур синтаксис выглядит так:

public struct MyDerivedStruct: Ilnterfacel, IInterface2 < // и т.д. >

Если при определении класса базовый класс не указан, то компилятор С# предполагает, что базовым классом является System.Object. Поэтому следующие два фрагмента кода эквивалентны:

class MyClass: Object // наследуется от System.Object < // и т.д. >

class MyClass // наследуется от System.Object

Для простоты чаще применяется вторая форма.

Поскольку в С# поддерживается ключевое слово object, служащее псевдонимом класса System.Object, можно записать и так:

class MyClass: object // наследуется от System.Object

Чтобы сослаться на класс Object, используйте ключевое слово object, которое распознается интеллектуальными редакторами вроде Visual Studio .NET. Это облегчит редактирование кода.

C++. Наследование в интерфейсах

Всем доброго вечера!

Вообще интерфейсы я использую для скрытия реализации (особенно хорошо когда пишешь библиотеку). Делаю я их всегда вот таким вот образом:

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

Очень долго я пользовался этим способом, пока сегодня в результате рефакторинга не наткнулся на ошибку. В общем решил я общее поведение у классов вынести в один класс и сделать для него интерфейс. И нужно было чтобы у интерфейсов начальных классов содержались функции интерфейса общих классов. Вот тут и началось самое интересное. Предположим что у каждого из классов (точнее у нескольких классов) должен быть ID. Для того, чтобы в каждом классе не писать данную реализацию, можно это дело вынести в отдельный класс и наследоваться от него. Правда в этом случае ID будет уже уникальный, хотя в прежнем варианте он был бы разный только для экземпляров одного класса. Ну что же, поехали, пишем интерфейс:

Читайте так же:  Налог ваз 2115

Теперь напишем класс, который будет его реализовывать:

Теперь напишем какой-то класс, который должен иметь ID. Сначала начнём с интерфейса:

Теперь саму реализацию:

Ну и собственно где-то там в коде пользователь хочет создать клиента по такой вот аналогии:

Но я получаю ошибку на строчке:

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

Мне тут сегодня подсказали другое применение интерфейсов (удивительно что я до сих пор про это не знал). Например нам нужен такой интерфейс:

Тогда для него мы пишем следующую реализацию:

А если потребуется создать и предоставить доступ, то:

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

  • класс GID для предоставления ID и его интерфейс IGID;
  • класс Param для пользовательских данных и его интерфейс IParam;
  • класс Object какого-то функционала и его интерфейс IObject

При этом Object должен обладать функционалом классов GID и Pointer, а его интерфейс IObject должен позволять ему обращаться к функционалу GID и Pointer. Ну что же, начнём по классам. GID и IGID:

Object и IObject:

Теперь проверим что же мы можем получить через эти интерфейсы:

Через IGID видим:

Через IParam видим:

Через IObject видим:

Хотя чему удивляться, мы бы так и так это увидели. А вот интереснее когда вызвать:

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

В общем что-то не так и возможно с порядком наследования или ещё чего-то (например порядок объявления функций). Подскажите как решить данную проблему?

Да и вообще, мне больше по душе первый вариант, но с ним не получилось (собственно как и со вторым вариантом). А вообще посоветуйте как правильно делать в таких ситуациях. Приведите примеры.

Спасибо за внимание!

Вы пропускаете virtual (во всех случаях)

Mikey
> должно быть так
не должно

s3dworld
не ромбовидное наследование, у тебя два предка IGID, только для одного из которых определена виртуальная функция
> Как можно решить данную проблему?
1) см виртуальное наследование
либо 2) не стоит интерфейс IClient наследовать от IGID

вторую половину поста не осилил, много букав

s3dworld
Да. Ты в такую тему залез. Я с ней разбирался месяц.
1. Наследование интерфейса и наследование реализации интерфейса две разные вещи.
2. Наследование реализации интерфейса делают через включение или агрегирование.
3. Определение интерфейсов делают через interface, а не через класс. Где то interface — struct, а где-то отдельный тип данных.
Пример:

Лучше поймешь как работать с интерфейсам.
Я у себя всю реализацию скрыл за интерфейсами. Загнал все это хозяйство в DLL. Теперь хоть из C++ юзают, хоть Delphi, VB.

Начёт слова virtualв потомках — оно опционально. Если у родителя метод объявлен как virtual он будет таким во всех потомках:

Из первой части да — походу тебе просто нужно виртуальное наследование.
Вторую тоже не осилил.

Читайте так же:  Приказ no 302 от 120411

Спасибо всем за ответы. Сегодня уже нет желания вникать в суть дела — пойду отдыхать. Но завтра обязательно досконально ознакомлюсь с предоставленными мне сведениями. Но так же я заметил что тут некоторые не читали вторую часть. Хотя мне очень интересно что Вы думаете по второму способу. Лично мне он противен, но человек который мне про него рассказал смеялся надо мной что я до этого использовал не этот способ, а первый способ. Поэтому хочется Ваших комментариев.

Если честно, то, что вы написали для меня(!) выглядит просто ужасно. Если бы мне надо было бы сделать что-то похожее, я бы делал примерно так

Sambuko
Ну в твоём коде приходится в ручную создавать каждую из функций у интерфейсов для каждого класса. В этом теряется прелесть не дублирования кода.

Мне кажется, вы не совсем понимаете назначение интерфейсов. Они нужны, чтобы отделять объявление от реализации. Если вам нужно, чтобы все классы действовали одинаково, то в интерфейсах нет необходимости:

Sambuko
Хорошо, объясню другими словами. У меня есть классы. Эти классы скрыты от пользователя, потому что я пишу библиотеку. Пользователю предоставляется только интерфейс, который управляет неким скрытым от пользователя объектом где-то там в памяти с непонятной для других структурой. Таким классов несколько. И у этих скрытых классов есть общее поведение. Но скажем так, нельзя взять и всё их общее поведение выделить в отдельный класс, так как сами по себе эти скрытые классы выполняют различные задачи и их общее поведение совпадает, скажем так, не в ровной степени. То есть, например, из 10 скрытых классов у 4 классов есть общее поведение типа A, у 3 классов общее поведение типа B, у двух классов общее поведение типа C. И у некоторых классов может быть составной набор поведений. Например один класс вообще не содержит общего поведения никакого типа, другой же класс содержит поведения A и C, другой только C, ещё несколько классов только A и B. То есть я хочу описать поведения отдельно, а уже классам их выдавать как спички ломать. И это не сложно. Но у меня задача этим не заканчивается. Дело в том, что интерфейс управляет поведением скрытого класса (экземпляра класса если кому угодно). Но он должен управлять не только индивидуальными особенностями класса, а так же должен уметь управлять теми общими чертами, которые унаследованы классом (те самые A, B и C). Следовательно было бы удобно для каждого типа общего поведения сделать свой интерфейс. А интерфейсу скрытого класса позволить наследоваться от интерфейса поведения, так же, как мы добавляем поведение скрытому классу через наследование. Ну я так и попытался сделать, однако ошибка.

Sambuko
> Если честно, то, что вы написали для меня(!) выглядит просто ужасно.
Вот и я про тоже.
Особенно это:

Наследование интерфейсов

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

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

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

Обратите внимание, что класс MyOperation реализует методы обоих интерфейсов, иначе возникла бы ошибка при компиляции:

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

Наследование интерфейса и наследование реализации

Сервис бесплатной оценки стоимости работы

  1. Заполните заявку. Специалисты рассчитают стоимость вашей работы
  2. Расчет стоимости придет на почту и по СМС

Номер вашей заявки

Прямо сейчас на почту придет автоматическое письмо-подтверждение с информацией о заявке.

Наследование в С#

Наследование интерфейса и наследование реализации
Начал читать книгу GoF и сразу же в предисловии попал в тупик. Чем отличается.

Читайте так же:  Приказ об отмене соревнований

Наследование С# , наследование полей
В классе есть приватное ПОЛЕ . Мне нужно использовать его в классе потомке .

Наследование
Подскажите как получить доступ к полю базового класса при таком примере class.

Наследование
Доброго времени суток!У меня возник небольшой вопрос по наследованию:как можно.

Наследование
При наследовании класса, все что реализовано в родительском, то будет.

Ключевое противоречие ООП

Как известно, классическое ООП покоится на трех китах:

Классическая же реализация по умолчанию:

  1. Инкапсуляция — публичные и приватные члены класса
  2. Наследование — реализация функционала за счет расширения одного класса-предка, защищенные члены класса.
  3. Полиморфизм — виртуальные методы класса-предка.

Наследование ломает инкапсуляцию

В теории мы уже имеем былинный отказ, но как насчет практики?

  1. Зависимость, создаваемая наследованием, чрезвычайно сильна;
  2. Наследники гиперчувствительны к любым изменениям предка;
  3. Наследование от чужого кода добавляет адскую боль при сопровождении: разработчики библиотеки рискуют получить обструкцию из-за поломанной обратной совместимости при малейшем изменении базового класса, а прикладники — регрессию при любом обновлении используемых библиотек.

Все, кто используют фреймворки, требующие наследования от своих классов (WinForms, WPF, WebForms, ASP.NET), легко найдут подтверждения всем трем пунктам в своем опыте.
Неужели все так плохо?

Теоретическое решение

Влияние проблемы можно ослабить принятием некоторых конвенций:

1. Защищенные члены не нужны
Это соглашение ликвидирует пабликов морозовых как класс.

2. Виртуальные методы предка ничего не делают
Это соглашение позволяет сочетать знание о реализации предка с независимостью от нее реализации уже в потомке.

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

4. Экземпляры предка никогда не создаются
Это соглашение позволяет избавиться от несоответствия требований к виртуальными методам (публичный контракт класса) с одной стороны и обязанностью ничего не делать (защищенный контракт класса) с другой. Теперь принцип подстановки Лисков можно соблюсти, не вступая в порочную связь с закрытым содержимым предка.

5. Невиртуальных членов у предка нет
С учетом предыдущих соглашений невиртуальные члены предка становятся бесполезными и подлежат ликвидации.

Результат: если класс-предок состоит из публичных виртуальных пустых методов и требований к ним для потомков, то наследование уже не ломает инкапсуляцию. Что и требовалось доказать.

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

Практические решения

PS: Дополнения и критика традиционно приветствуются.

Еще статьи:

  • Отказываемся с вами заключить договор сопровождения Эксклюзивный договор, особенности Сейчас не редко встречаются ситуации, когда сделка с недвижимостью оспаривается и признается недействительной. В основном это происходит из-за жульнических поступков преступных элементов, при неграмотном подходе к проведению сделки и так далее. Именно […]
  • Адвокат марк ханиф Свобода стоит 240 тысяч Деньги на залог для Ассанжа приходится собирать “всем миром” 15.12.2010 в 20:31, просмотров: 2813 Основатель сайта WikiLeaks Джулиан Ассанж по-прежнему находится в тюрьме на юге Лондона, хотя во вторник магистратский суд Вестминстера принял решение о его […]
  • Размер единовременного пособия на погребение в 2019 году Размер и особенности выплаты пособия на погребение Пособие на погребение – это специальная социальная выплата, призванная компенсировать похоронные расходы. Ее выдают льготным категориям граждан, которые не могут самостоятельно оплатить траурную церемонию. Мера призвана поддержать […]
  • Информ патент ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "АГЕНТСТВО ИНТЕЛЛЕКТУАЛЬНОЙ СОБСТВЕННОСТИ ПОЛИ-ИНФОРМ-ПАТЕНТ" 191040, г Санкт-Петербург, проспект Лиговский, дом 87 ЛИТЕР А, ПОМЕЩЕНИЕ 18-Н ОФИС 521 Основной род деятельности ООО "АИС ПОЛИ-ИНФОРМ-ПАТЕНТ": Деятельность по предоставлению прочих […]
  • Договор купли продажи участка по доверенности образец Договор купли продажи земельного участка по доверенности образец 2018 между физ лицами Это быстро и бесплатно! Содержание: Может ли доверенное лицо провести сделку? Законодательные нормы Виды доверительных документов Какой из них подходит для заключения соглашения? Требования […]
  • Договор подряда тесты Бесплатные тесты с ответами Гражданское право. Тест 9 1. Предметом договора подряда является трудовая деятельность подрядчика выполнение определенной работы с получением конкретного материального результата научные исследования производство сельскохозяйственной продукции действия […]