Наследование (объектно-ориентированное программирование)
Эту статью может потребовать очистки Википедии , чтобы она соответствовала стандартам качества . Конкретная проблема: Захламлено. ( Апрель 2015 г. ) |
В объектно-ориентированном программировании наследование — это механизм создания объекта или класса на основе другого объекта ( наследование на основе прототипа ) или класса ( наследование на основе классов ) с сохранением аналогичной реализации . Также определяется как получение новых классов ( подклассов ) из существующих, таких как суперкласс или базовый класс , а затем формирование их в иерархию классов. В большинстве объектно-ориентированных языков на основе классов, таких как C++ , объект, созданный посредством наследования, «дочерний объект», приобретает все свойства и поведение «родительского объекта», за исключением: конструкторов , деструкторов, перегруженных операторов и других. функции базового класса. Наследование позволяет программистам создавать классы, построенные на основе существующих классов. [1] указать новую реализацию, сохраняя при этом то же поведение ( реализуя интерфейс ), повторно использовать код и независимо расширять исходное программное обеспечение через общедоступные классы и интерфейсы . Отношения объектов или классов посредством наследования порождают направленный ациклический граф .
Унаследованный класс называется подклассом родительского класса или суперкласса. Термин «наследование» широко используется как для программирования на основе классов, так и для программирования на основе прототипов, но в узком смысле этот термин зарезервирован для программирования на основе классов (один класс наследуется от другого), при этом соответствующий метод программирования на основе прототипов вместо этого называется делегированием (один объект делегирует другому). Шаблоны наследования, изменяющие классы, могут быть предварительно определены в соответствии с простыми параметрами сетевого интерфейса, что обеспечивает сохранение межъязыковой совместимости. [2] [3]
Наследование не следует путать с подтипированием . [4] [5] В некоторых языках наследование и подтипирование согласованы. [а] тогда как в других они различаются; в общем, подтипирование устанавливает отношение is-a , тогда как наследование только повторно использует реализацию и устанавливает синтаксические отношения, не обязательно семантические отношения (наследование не обеспечивает поведенческое подтипирование). Чтобы различать эти концепции, подтипирование иногда называют наследованием интерфейса (без учета того, что специализация переменных типа также вызывает отношение подтипирования), тогда как наследование, определенное здесь, известно как наследование реализации или наследование кода . [6] Тем не менее, наследование является широко используемым механизмом для установления отношений подтипов. [7]
Наследование противопоставляется композиции объектов , где один объект содержит другой объект (или объекты одного класса содержат объекты другого класса); см. композицию вместо наследования . Композиция реализует отношение «имеет-а» в отличие от отношения «есть-а» подтипа.
История [ править ]
В 1966 году Тони Хоар представил некоторые замечания о записях и, в частности, представил идею подклассов записей, типов записей с общими свойствами, но отличающихся тегом варианта и имеющих поля, частные для варианта. [8] Под влиянием этого в 1967 году Оле-Йохан Даль и Кристен Найгаард представили дизайн, который позволял определять объекты, принадлежавшие к разным классам, но имеющие общие свойства. Общие свойства были собраны в суперклассе, и каждый суперкласс потенциально мог иметь суперкласс. Таким образом, значения подкласса представляли собой составные объекты, состоящие из некоторого количества префиксных частей, принадлежащих различным суперклассам, плюс основной части, принадлежащей подклассу. Все эти части были объединены воедино. [9] Атрибуты составного объекта будут доступны через точечную запись. Эта идея была впервые реализована в языке программирования Simula 67. [10] Затем идея распространилась на Smalltalk , C++ , Java , Python и многие другие языки.
Типы [ править ]
Существуют различные типы наследования, основанные на парадигме и конкретном языке. [11]
- Единое наследование
- где подклассы наследуют функции одного суперкласса. Класс приобретает свойства другого класса.
- Множественное наследование
- где один класс может иметь более одного суперкласса и наследовать функции всех родительских классов.
«Множественное наследование ... считалось очень трудным для эффективной реализации. Например, в кратком изложении C++ в своей книге по Objective C Брэд Кокс фактически утверждал, что добавление множественного наследования в C++ невозможно. Таким образом, множественное наследование казалось невозможным. Это более сложная задача, поскольку я рассматривал множественное наследование еще в 1982 году и нашел простой и эффективный метод реализации в 1984 году, я подозреваю, что это единственный случай, когда мода влияет на последовательность событий. ." [12]
- Многоуровневое наследование
- где подкласс наследуется от другого подкласса. Нередко класс является производным от другого производного класса, как показано на рисунке «Многоуровневое наследование».
- Класс A служит базовым классом для производного класса B в свою очередь, служит базовым классом для производного класса C. , который , Класс B известен как промежуточный поскольку он обеспечивает связь для наследования между A и C. базовый класс , Цепочка ABC известна как путь наследования .
- Производный класс с многоуровневым наследованием объявляется следующим образом:
// C++ language implementation class A { ... }; // Base class class B : public A { ... }; // B derived from A class C : public B { ... }; // C derived from B
- Этот процесс можно распространить на любое количество уровней.
- Иерархическое наследование
- Здесь один класс служит суперклассом (базовым классом) для более чем одного подкласса. Например, родительский класс A может иметь два подкласса B и C. Родительским классом B и C является A, но B и C — два отдельных подкласса.
- Гибридное наследование
- Гибридное наследование — это сочетание двух или более вышеуказанных типов наследования. Примером этого является случай, когда класс A имеет подкласс B, который имеет два подкласса, C и D. Это смесь как многоуровневого наследования, так и иерархического наследования.
Подклассы и суперклассы [ править ]
Подклассы , производные классы , классы-наследники или дочерние классы — это модульные производные классы, которые наследуют одну или несколько языковых сущностей от одного или нескольких других классов (называемых суперклассом , базовыми классами или родительскими классами ). Семантика наследования классов варьируется от языка к языку, но обычно подкласс автоматически наследует переменные экземпляра и функции-члены своих суперклассов.
Общая форма определения производного класса: [13]
class SubClass: visibility SuperClass
{
// subclass members
};
- Двоеточие указывает, что подкласс наследуется от суперкласса. Видимость не является обязательной и, если она присутствует, может быть частной или публичной . Видимость по умолчанию — частная . Видимость определяет, являются ли функции базового класса частными или общедоступными .
Некоторые языки также поддерживают наследование других конструкций. Например, в контракты Eiffel , определяющие спецификацию класса, также наследуются наследниками. Суперкласс устанавливает общий интерфейс и базовую функциональность, которую специализированные подклассы могут наследовать, изменять и дополнять. Программное обеспечение, унаследованное подклассом, считается повторно используемым в подклассе. Ссылка на экземпляр класса на самом деле может относиться к одному из его подклассов. Фактический класс объекта, на который ссылаются, невозможно предсказать во время компиляции . Унифицированный интерфейс используется для вызова функций-членов объектов ряда различных классов. Подклассы могут заменять функции суперкласса совершенно новыми функциями, которые должны иметь ту же сигнатуру метода .
Классы, не подклассифицируемые [ править ]
В некоторых языках класс может быть объявлен как не подклассифицируемый путем добавления определенных модификаторов класса к объявлению класса. Примеры включают final
ключевое слово в Java и C++11 и далее или sealed
Ключевое слово в C#. Такие модификаторы добавляются в объявление класса перед class
ключевое слово и объявление идентификатора класса. Такие не подклассифицированные классы ограничивают возможность повторного использования , особенно когда разработчики имеют доступ только к предварительно скомпилированным двоичным файлам , а не к исходному коду .
можно легко сделать вывод Класс, не имеющий подклассов, не имеет подклассов, поэтому во время компиляции , что ссылки или указатели на объекты этого класса на самом деле ссылаются на экземпляры этого класса, а не на экземпляры подклассов (они не существуют) или экземпляры суперклассов ( преобразование ссылочного типа нарушает систему типов). Поскольку точный тип объекта, на который ссылаются, известен до выполнения, раннее связывание (также называемое статической отправкой ) может использоваться вместо позднего связывания (также называемого динамической отправкой ), которое требует одного или нескольких таблицы виртуальных методов поисков в зависимости от того, является ли множественное наследование. или только одиночное наследование поддерживается на используемом языке программирования.
Непереопределяемые методы [ править ]
Точно так же, как классы не могут быть подклассами, объявления методов могут содержать модификаторы метода, которые предотвращают переопределение метода (т. е. замену новой функцией с тем же именем и сигнатурой типа в подклассе). Закрытый метод не переопределяется просто потому, что он недоступен для классов, отличных от класса , членом которого он является (однако это неверно для C++). А final
метод в Java, sealed
метод на C# или frozen
функцию в Eiffel нельзя переопределить.
Виртуальные методы [ править ]
Если метод суперкласса является виртуальным методом , то вызовы метода суперкласса будут выполняться динамически . Некоторые языки требуют, чтобы метод был специально объявлен как виртуальный (например, C++), а в других все методы являются виртуальными (например, Java). Вызов невиртуального метода всегда будет статически отправлен (т.е. адрес вызова функции определяется во время компиляции). Статическая диспетчеризация выполняется быстрее динамической и допускает такие оптимизации, как встроенное расширение .
Видимость унаследованных участников [ править ]
В следующей таблице показано, какие переменные и функции наследуются в зависимости от видимости, заданной при создании класса, с использованием терминологии, установленной C++. [14]
Видимость базового класса | Видимость производного класса | ||
---|---|---|---|
Частное происхождение | Защищенный вывод | Публичное происхождение | |
|
|
|
|
Приложения [ править ]
Наследование используется для связи двух или более классов друг с другом.
Переопределение [ править ]
Многие объектно-ориентированные языки программирования позволяют классу или объекту заменять реализацию аспекта (обычно поведения), который он унаследовал. Этот процесс называется переопределением . Переопределение приводит к усложнению: какую версию поведения использует экземпляр унаследованного класса — ту, которая является частью его собственного класса, или ту, которая принадлежит родительскому (базовому) классу? Ответ варьируется в зависимости от языка программирования, и некоторые языки предоставляют возможность указать, что определенное поведение не должно быть переопределено и должно вести себя так, как определено базовым классом. Например, в C# базовый метод или свойство можно переопределить в подклассе только в том случае, если он помечен модификатором virtual, Abstract или override, тогда как в таких языках программирования, как Java, можно вызывать различные методы для переопределения других методов. [15] Альтернативой переопределению является сокрытие унаследованного кода.
Повторное использование кода [ править ]
Наследование реализации — это механизм, посредством которого подкласс повторно использует код базового класса. По умолчанию подкласс сохраняет все операции базового класса, но подкласс может переопределять некоторые или все операции, заменяя реализацию базового класса своей собственной.
В следующем примере Python подклассы SquareSumКомпьютер и CubeSumComputer переопределяет метод Transform() базового класса СуммаКомпьютер . Базовый класс содержит операции по вычислению суммы квадратов двух целых чисел. Подкласс повторно использует всю функциональность базового класса, за исключением операции, преобразующей число в квадрат, заменяя ее операцией, преобразующей число в квадрат и куб соответственно. Таким образом, подклассы вычисляют сумму квадратов/кубов между двумя целыми числами.
Ниже приведен пример Python.
class SumComputer:
def __init__(self, a, b):
self.a = a
self.b = b
def transform(self, x):
raise NotImplementedError
def inputs(self):
return range(self.a, self.b)
def compute(self):
return sum(self.transform(value) for value in self.inputs())
class SquareSumComputer(SumComputer):
def transform(self, x):
return x * x
class CubeSumComputer(SumComputer):
def transform(self, x):
return x * x * x
В большинстве случаев наследование классов с единственной целью повторного использования кода вышло из моды. [ нужна ссылка ] Основная проблема заключается в том, что наследование реализации не обеспечивает никакой гарантии полиморфной заменяемости — экземпляр повторно используемого класса не обязательно может быть заменен экземпляром унаследованного класса. Альтернативный метод, явное делегирование , требует больше усилий по программированию, но позволяет избежать проблемы подстановки. [ нужна ссылка ] В C++ частное наследование может использоваться как форма наследования реализации без возможности замены. В то время как публичное наследование представляет собой отношение «есть-а», а делегирование представляет отношение «есть-а», частное (и защищенное) наследование можно рассматривать как отношение «реализовано с точки зрения». [16]
Другое частое использование наследования — гарантировать, что классы поддерживают определенный общий интерфейс; то есть они реализуют одни и те же методы. Родительский класс может представлять собой комбинацию реализованных операций и операций, которые должны быть реализованы в дочерних классах. Часто интерфейс между супертипом и подтипом не меняется — дочерний тип реализует описанное поведение вместо родительского класса. [17]
Наследование против подтипирования [ править ]
Наследование похоже на подтипирование, но отличается от него . [4] данный тип Подтипирование позволяет заменить другим типом или абстракцией и, как говорят, устанавливает связь между подтипом и некоторой существующей абстракцией, явно или неявно, в зависимости от языковой поддержки. Отношения могут быть выражены явно через наследование в языках, которые поддерживают наследование как механизм подтипирования. Например, следующий код C++ устанавливает явные отношения наследования между классами B и A , где B является одновременно подклассом и подтипом A и может использоваться как A везде, где указан B (через ссылку, указатель или сам объект).
class A {
public:
void DoSomethingALike() const {}
};
class B : public A {
public:
void DoSomethingBLike() const {}
};
void UseAnA(const A& a) {
a.DoSomethingALike();
}
void SomeFunc() {
B b;
UseAnA(b); // b can be substituted for an A.
}
В языках программирования, которые не поддерживают наследование в качестве механизма подтипирования , связь между базовым классом и производным классом представляет собой только связь между реализациями (механизм повторного использования кода), в отличие от связи между типами . Наследование, даже в языках программирования, которые поддерживают наследование как механизм создания подтипов, не обязательно влечет за собой создание поведенческих подтипов . Вполне возможно получить класс, объект которого будет вести себя неправильно при использовании в контексте, где ожидается родительский класс; см. принцип замены Лискова . [18] (Сравните коннотацию/обозначение .) В некоторых языках ООП понятия повторного использования кода и подтипирования совпадают, поскольку единственный способ объявить подтип — это определить новый класс, который наследует реализацию другого.
дизайна Ограничения
Широкое использование наследования при разработке программы накладывает определенные ограничения.
Например, рассмотрим класс Лицо , содержащее имя человека, дату рождения, адрес и номер телефона. Мы можем определить подкласс Человек позвонил Студент , который содержит средний балл человека и пройденные занятия, а также еще один подкласс Человек позвонил Сотрудник , который содержит название должности, работодателя и зарплату человека.
При определении этой иерархии наследования мы уже определили некоторые ограничения, не все из которых желательны:
- Одиночество
- Используя одиночное наследование, подкласс может наследовать только от одного суперкласса. Продолжая приведенный выше пример, А. Объект Person может быть либо Студент или Сотрудник , но не оба. Использование множественного наследования частично решает эту проблему, поскольку тогда можно определить Класс StudentEmployee , который наследуется от обоих Студент и Сотрудник . Однако в большинстве реализаций он по-прежнему может наследовать от каждого суперкласса только один раз и, таким образом, не поддерживает случаи, когда студент работает на двух работах или посещает два учреждения. Модель наследования, доступная в Eiffel, делает это возможным благодаря поддержке повторного наследования .
- Статический
- Иерархия наследования объекта фиксируется при создании экземпляра , когда выбран тип объекта, и не меняется со временем. Например, граф наследования не позволяет Студент возражает против того, чтобы стать Объект сотрудника , сохраняя при этом состояние своего Суперкласс человека . (Однако такого поведения можно добиться с помощью шаблона декоратора .) Некоторые критикуют наследование, утверждая, что оно привязывает разработчиков к их первоначальным стандартам проектирования. [19]
- Видимость
- Всякий раз, когда клиентский код имеет доступ к объекту, он обычно имеет доступ ко всем данным суперкласса объекта. Даже если суперкласс не объявлен общедоступным, клиент все равно может привести объект к типу его суперкласса. Например, невозможно передать функции указатель на . Средний балл учащегося и транскрипт без предоставления этой функции доступа ко всем личным данным, хранящимся в личном кабинете учащегося Суперкласс человека . Многие современные языки, включая C++ и Java, предоставляют модификатор «защищенного» доступа, который позволяет подклассам получать доступ к данным, не позволяя доступ к ним какому-либо коду вне цепочки наследования.
Принцип составного повторного использования является альтернативой наследованию. Этот метод поддерживает полиморфизм и повторное использование кода, отделяя поведение от основной иерархии классов и включая определенные классы поведения, необходимые в любом классе бизнес-домена. Этот подход позволяет избежать статической природы иерархии классов, позволяя изменять поведение во время выполнения, и позволяет одному классу реализовывать поведение в виде шведского стола, а не ограничиваться поведением своих классов-предков.
Проблемы и альтернативы [ править ]
Наследование реализации вызывает споры среди программистов и теоретиков объектно-ориентированного программирования, по крайней мере, с 1990-х годов. Среди них есть авторы Design Patterns , которые вместо этого выступают за наследование интерфейсов и отдают предпочтение композиции, а не наследованию. Например, шаблон декоратора (как упоминалось выше ) был предложен для преодоления статической природы наследования между классами. В качестве более фундаментального решения той же проблемы ролевое программирование вводит особые отношения, объединяющие свойства наследования и композиции в новую концепцию. [ нужна ссылка ]
По словам Аллена Голуба , основная проблема наследования реализации заключается в том, что оно вводит ненужную связь в форме «проблемы хрупкого базового класса» : [6] модификации реализации базового класса могут привести к непреднамеренным изменениям поведения подклассов. Использование интерфейсов позволяет избежать этой проблемы, поскольку не используется общая реализация, а только API. [19] Другой способ выразить это так: «наследование нарушает инкапсуляцию ». [20] Проблема явно проявляется в открытых объектно-ориентированных системах, таких как фреймворки , где ожидается, что клиентский код будет наследовать от классов, предоставляемых системой, а затем заменять классы системы в ее алгоритмах. [6]
Сообщается, что изобретатель Java Джеймс Гослинг выступил против наследования реализации, заявив, что он не стал бы включать его, если бы ему пришлось перепроектировать Java. [19] Языковые конструкции, отделяющие наследование от подтипов (наследование интерфейса), появились еще в 1990 году; [21] современным примером этого является язык программирования Go .
Сложное наследование или наследование, используемое в недостаточно зрелом проекте, может привести к проблеме йо-йо . Когда в конце 1990-х годов наследование использовалось в качестве основного подхода к структурированию программ, разработчики имели тенденцию разбивать код на большее количество уровней наследования по мере роста функциональности системы. Если команда разработчиков объединила несколько уровней наследования с принципом единой ответственности, это привело к появлению множества очень тонких слоев кода, многие из которых состояли всего из 1 или 2 строк реального кода. [ нужна ссылка ] Слишком большое количество слоев усложняет отладку, поскольку становится сложно определить, какой уровень необходимо отлаживать.
Другая проблема с наследованием заключается в том, что подклассы должны быть определены в коде, а это означает, что пользователи программы не могут добавлять новые подклассы во время выполнения. Другие шаблоны проектирования (например, Entity-Component-system ) позволяют пользователям программы определять варианты сущности во время выполнения.
См. также [ править ]
- Образец архетипа
- Задача круг-эллипс
- Оправданное рассуждение - рассуждение, которое рационально убедительно, но не дедуктивно обосновано.
- Интерфейс (вычисления) - общая граница между элементами вычислительной системы.
- Переопределение метода - функция языка в объектно-ориентированном программировании.
- Mixin - класс объектно-ориентированных языков программирования.
- Полиморфизм (информатика) – использование одного интерфейса или символа для нескольких разных типов.
- Протокол — абстракция страниц класса,
- Ролевое программирование - парадигма программирования, основанная на концептуальном понимании объектов.
- Черта (компьютерное программирование) - концепция, используемая в объектно-ориентированном программировании.
- Виртуальное наследование — техника на языке C++.
Примечания [ править ]
Ссылки [ править ]
- ^ Джонсон, Ральф (26 августа 1991 г.). «Проектирование повторно используемых классов» (PDF) . www.cse.msu.edu .
- ^ Мэдсен, О.Л. (1989). «Виртуальные классы: мощный механизм объектно-ориентированного программирования». Материалы конференции «Объектно-ориентированные системы, языки и приложения программирования — OOPSLA '89» . стр. 397–406. дои : 10.1145/74877.74919 . ISBN 0897913337 . S2CID 1104130 .
- ^ Дэвис, Терк (2021). Передовые методы и глубокое обучение в компьютерном зрении . Эльзевир Наука. стр. 179–342.
- ^ Jump up to: а б Кук, Уильям Р.; Хилл, Уолтер; Каннинг, Питер С. (1990). Наследование не является подтипом . Материалы 17-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования (POPL). стр. 125–135. CiteSeerX 10.1.1.102.8635 . дои : 10.1145/96709.96721 . ISBN 0-89791-343-4 .
- ^ Карделли, Лука (1993). Типовое программирование (Технический отчет). Корпорация цифрового оборудования . п. 32–33. Отчет об исследовании SRC 45.
- ^ Jump up to: а б с Михайлов, Леонид; Секерински, Эмиль (1998). Исследование проблемы хрупкого базового класса (PDF) . Материалы 12-й Европейской конференции по объектно-ориентированному программированию (ECOOP). Конспекты лекций по информатике. Том. 1445. Спрингер. стр. 355–382. дои : 10.1007/BFb0054099 . ISBN 978-3-540-64737-9 . Архивировано из оригинала (PDF) 13 августа 2017 г. Проверено 28 августа 2015 г.
- ^ Темперо, Юэн; Ян, Хун Юл; Ноубл, Джеймс (2013). Что программисты делают с наследованием в Java (PDF) . ЭКООП 2013–Объектно-ориентированное программирование. Конспекты лекций по информатике. Том. 7920. Спрингер. стр. 577–601. дои : 10.1007/978-3-642-39038-8_24 . ISBN 978-3-642-39038-8 .
- ^ Хоар, ЦАР (1966). Обработка записей (PDF) (Технический отчет). стр. 15–16.
- ^ Даль, Оле-Йохан; Найгаард, Кристен (май 1967 г.). Объявления классов и подклассов (PDF) . Рабочая конференция ИФИП по языкам моделирования. Осло: Норвежский вычислительный центр.
- ^ Даль, Оле-Йохан (2004). «Рождение объектной ориентации: языки моделирования» (PDF) . Конспекты лекций по информатике. Том. 2635. стр. 15–25. дои : 10.1007/978-3-540-39993-3_3 . ISBN 978-3-540-21366-6 .
{{cite book}}
:|journal=
игнорируется ( помощь ) ; Отсутствует или пусто|title=
( помощь ) - ^ «Наследование С++» . www.cs.nmsu.edu .
- ^ Страуструп, Бьярн (1994). Проектирование и эволюция C++ . Пирсон. п. 417. ИСБН 9780135229477 .
- ^ Шильдт, Герберт (2003). Полный справочник по C++ . Тата МакГроу Хилл. п. 417 . ISBN 978-0-07-053246-5 .
- ^ Балагурусами, Э. (2010). Объектно-ориентированное программирование на C++ . Тата МакГроу Хилл. п. 213. ИСБН 978-0-07-066907-9 .
- ^ переопределить (Справочник по C#)
- ^ «GotW #60: Проектирование классов с защитой от исключений, Часть 2: Наследование» . Gotw.ca. Проверено 15 августа 2012 г.
- ^ Венугопал, КР; Буя, Раджкумар (2013). Освоение С++ . Тата МакГроу Хилл Образовательная Частная Лимитед. п. 609. ИСБН 9781259029943 .
- ^ Митчелл, Джон (2002). «10 «Концепции объектно-ориентированных языков» ». Понятия в языке программирования . Издательство Кембриджского университета. п. 287 . ISBN 978-0-521-78098-8 .
- ^ Jump up to: а б с Голуб, Аллен (1 августа 2003 г.). «Почему расширяется – это зло» . Проверено 10 марта 2015 г.
- ^ Зейтер, Линда М.; Палсберг, Йенс; Либерхерр, Карл Дж. (1996). «Эволюция поведения объектов с помощью контекстных отношений» . Заметки по разработке программного обеспечения ACM SIGSOFT . 21 (6): 46. CiteSeerX 10.1.1.36.5053 . дои : 10.1145/250707.239108 .
- ^ Америка, Пьер (1991). Проектирование объектно-ориентированного языка программирования с поведенческим подтипированием . Школа/семинар REX по основам объектно-ориентированных языков. Конспекты лекций по информатике. Том. 489. стр. 60–90. дои : 10.1007/BFb0019440 . ISBN 978-3-540-53931-5 .
Дальнейшее чтение [ править ]
- Мейер, Бертран (1997). «24. Правильное использование наследования». Объектно-ориентированное построение программного обеспечения (2-е изд.). Прентис Холл. ISBN 0-13-629155-4 .
- Самохин, Вадим (2017). «Наследование реализации — это зло» . ХакерНун . Середина.