Наслідування (програмування)![]() Успадкування (англ. inheritance, „наслідування“ це помилкова назва, калькована з рос. наследование) — один з принципів об'єктно-орієнтовного програмування[1], який дає класу можливість використовувати програмний код іншого (базового) класу[2], доповнюючи його своїми власними деталями реалізації. Іншими словами, під час успадкування відбувається отримання нового (похідного) класу, який містить програмний код базового класу з зазначенням власних особливостей використання. Успадкування належить до типу is-a відношень між класами. При успадкуванні створюється спеціалізована версія вже наявного класу. В Unified Modeling Language успадкування класів відображується на діаграми класів. Переваги використання успадкуванняПравильне використання механізму успадкування дає наступні взаємозв'язані переваги:
Недоліки використання наслідуванняПри використанні наслідування в програмах були помічені наступні недоліки:
ТермінологіяВ об'єктно-орієнтованому програмуванні, починаючи з Simula 67[3], абстрактні типи данних називаются класами. Базовий клас (англ. base class) — це клас, який перебуває на вершині ієрархії успадкування класів і в основі дерева підкласів, тобто не є підкласом і не має успадкувань від інших суперкласів або інтерфейсів. Базовим класом може бути абстрактний клас і інтерфейс. Будь-який не базовий клас є підкласом. Суперклас (англ. superclass), батьківський клас (англ. parent class), предок або надклас — клас, від якого виконується успадкування в підкласах, тобто клас, від якого спадкують інші класи. Суперкласом може бути підклас, базовий клас, абстрактний клас і інтерфейс. Підклас (англ. subclass), похідний клас (англ. derived class), дочірній клас (англ. child class), клас-нащадок або клас-реалізатор — клас, який спадкує від суперкласу або інтерфейсу, тобто клас визначений через успадкування від іншого класу або деяких таких класів. Підкласом може бути суперклас. Інтерфейс (англ. interface) — це структура, що визначає чистий інтерфейс класу, що складається з абстрактних методів. Інтерфейси беруть участь в ієрархії успадкування класів і інтерфейсів. Базовий інтерфейс (англ. base interface) — це аналог базового класу в ієрархії успадкування інтерфейсів, тобто це інтерфейс, який перебуває на вершині ієрархії успадкування. Суперінтерфейс (англ. super interface) або інтерфейс-предок — це аналог суперкласу в ієрархії успадкування, тобто від цього інтерфейсу виконується успадкування в підкласах і підінтерфейсах. Інтерфейс-нащадок або похідний інтерфейс (англ. derived interface) — це аналог підкласу в ієрархії успадкування інтерфейсів, тобто це інтерфейс, який спадкує від одного або декількох суперінтерфейсів. Ієрархія успадкування або ієрархія класів — дерево, елементами якого є класи та інтерфейси. ЗастосуванняУспадкування є механізмом повторного використання коду (англ. code reuse) і сприяє незалежному розширенню програмного забезпечення через відкриті класи (англ. public classes) та інтерфейси (англ. interfaces). Встановлення відношення успадкування між класами породжує ієрархію класів[en]. Типи успадкування«Просте» успадкування«Просте» успадкування, або одинарне успадкування, описує спорідненість між двома класами: один з яких спадкує від іншого. З одного класу може виводитися багато класів, але навіть в цьому випадку подібний вид взаємозвязку залишається «простим» успадкуванням. Абстрактні класи і створення об'єктівДля деяких мов програмування справедлива наступна концепція. Існують «абстрактні» класи (оголошуються такими довільно або через приписані до них абстрактні методи); їх можна описувати наявними поля та методи. Створення ж об'єктів (екземплярів) означає конкретизацію, застосовну тільки до неабстрактних класів (в тому числі, до неабстрактних нащадків абстрактних), — представниками яких, в результаті, будуть створені об'єкти. Множинне успадкуванняПри множинному успадкуванні класа може мати більш, ніж одного предка. В цьому випадку клас успадковує методи всіх предків. Переваги такого підходу в більшій гнучкості. Множинне успадкування реалізовано в C++. З інших мов, що надають цю можливість, можна відмітити Python і Eiffel. Множинне успадкування підтримується в мові UML. Множинне успадкування — потенційне джерело помилок, які можуть виникати через наявність однакових імен методів у предків. В мовах, які позиціонуются як спадкоємці C++ (Java, C# та інші), було прийнято рішення відмовитись від множинного успадкування на користь інтерфейсів. Практично завжди можна обійтися без використання даного механізму. Однак, якщо така необхідність усе ж виникла, то для розв'язання конфліктів використання успадкованих методів з однаковими іменами можливо, наприклад, застосувати операцію розширення видимості — «::» — для виклику конкретного метода конкретного предка. Спроба вирішення проблеми наявності однакових імен методів в предках була здійснена у мові Eiffel, в якій при описі нового класу необхідно явно вказати імпортовані члени кожного з успадкованих класів і їхні імена у дочірньому класі. Більшість сучасних об'єктно-орієнтованих мов програмуння (C#, Java, Delphi та інші) підтримують можливість одночасно успадковувати від класа-предка і реалізовувати методи декількох інтерфейсів одним і тим самим класом. Цей механізм дозволяє багато в чому замінити множинне успадкування — методи інтерфейсів необхідно перевизначати явно, що виключає помилки при успадкуванні функціональності однакових методів різних класів-предків. Єдиний базовий класВ ряді мов програмування, усі класи, — явно або неявно, — успадковуються від деякого базового класу. Smalltalk був одним з перших мов, в яких використовувалась ця концепція. До таких мов також відносяться: Objective-C ( Успадкування в мовах програмуванняУспадкування в C++[4]: class A {}; // Базовий клас
class B : public A {}; // Public-успадкування
class C : protected A {}; // Protected-успадкування
class Z : private A {}; // Private-успадкування
В C++ існує три типи успадкування: public, protected, private. Специфікатори доступу членів базового класу змінюються в потомках наступним чином: Якщо клас оголошено як базовий для іншого класу зі специфікатором доступу:
Одним з основних переваг public-успадкування є те, що вказівник на класи-нащадки може бути неявно перетворений у вказівник на базовий клас, тобто для прикладу вище можна написати: A* a = new B();
Ця цікава можливість відкриває можливість динамічної ідентифікації типу (RTTI). Delphi (Object Pascal)
Для використання механізму наслідування в Delphi необхідно в оголошені класу в дужках TAncestor = class
private
protected
public
//Віртуальна процедура
procedure VirtualProcedure; virtual; abstract;
procedure StaticProcedure;
end;
Наслідник: TDescendant = class(TAncestor)
private
protected
public
//Перекриття віртуальної процедуры
procedure VirtualProcedure; override;
procedure StaticProcedure;
end;
Абсолютно всі класи в Delphi є нащадками класа Множинне наслідування в Delphi з самого початку не підтримується, однак для тих, кому без цього не обійтись, усе ж є такі можливості, наприклад, за рахунок використання класів-помічників(англ. Class Helpers). Python підтримує як одиночне, так і множинне успадкування. При доступі до атрибуту, перегляд похідних класів прохидить в порядку розширення метода (англ. method resolution order, MRO). class Ancestor1(object): # Предок-1
def m1(self): pass
class Ancestor2(object): # Предок-2
def m1(self): pass
class Descendant(Ancestor1, Ancestor2): # Наслідник
def m2(self): pass
d = Descendant() # Ініціалізація
print d.__class__.__mro__ # Порядок розширення метода:
(<class '__main__.Descendant'>, <class '__main__.Ancestor1'>, <class '__main__.Ancestor2'>, <type 'object'>)
З версії Python 2.2[5] в мові існують «класичні» класи і «нові» класи. Останні є нащадками Множинне успадкування застосовують у Python для введення в основний клас класів-домішок (англ. mix-in).
Для використання механізма наслідування в PHP необхідно в оголошенні класу після імені оголошеного класу-наслідника вказати слово class Descendant extends Ancestor {
}
У випадку перекриття класом-нащадком методів предка доступ до методів предка можна отримати з використанням ключового слова class A {
function example() {
echo "Викликаний метод A::example().\n";
}
}
class B extends A {
function example() {
echo "Викликаний метод B::example().\n";
parent::example();
}
}
Можна запобігти перекриття класом-нащадком методів предка; для цього необхідно вказати ключове слово class A {
final function example() {
echo "Викликаний метод A::example().\n";
}
}
class B extends A {
function example() { //викличе помилку
parent::example(); //і ніколи не виконається
}
}
Щоб при успадкуванні звернутись до конструктора батьківського класу, необхідно дочірньому класу в конструкторі вказати @interface A : NSObject
- (void) example;
@end
@implementation
- (void) example
{
NSLog(@"Class A");
}
@end
@interface B : A
- (void) example;
@end
@implementation
- (void) example
{
NSLog(@"Class B");
}
@end
В інтерфейсі оголошують методи, які буде видно ззовні класу (public). Внутрішні методи можна реалізовувати без інтерфейсу. Для оголошення додаткових властивостей користуються interface-extension у файлі реалізації. Усі методи в Objective-C — віртуальні. Приклад успадкування від одного класу і двох інтерфейсів: public class A { }
public interface I1 { }
public interface I2 { }
public class B extends A implements I1, I2 { }
Директива Приклад успадкування[6] від одного класу і двох інтерфейсів: public class A { }
public interface I1 { }
public interface I2 { }
public class B : A, I1, I2 { }
Успадкування від типізованих класів можна здійснювати, вказавши фіксований тип або шляхом переносу змінної типу в успадкований клас: public class A<T>
{ }
public class B : A<int>
{ }
public class B2<T> : A<T>
{ }
Допускається також успадкування вкладених класів від класів, які їх містять: class A // default class A is internal, not public class B can not be public (клас A за замовчуванням є внутрішнім, не публічний клас B не може бути публічним)
{
class B : A { }
}
Директива class Parent
def public_method
"Public method"
end
private
def private_method
"Private method"
end
end
class Child < Parent
def public_method
"Redefined public method"
end
def call_private_method
"Ancestor's private method: " + private_method
end
end
Клас child = Child.new
child.public_method #=> "Redefined public method"
child.call_private_method #=> "Ancestor's private method: Private method"
Приватні методи предка можна викликати з нащадків. class Parent {
constructor(data) {
this.data = data;
}
publicMethod() {
return 'Public Method';
}
}
class Child extends Parent {
getData() {
return `Data: ${this.data}`;
}
publicMethod() {
return 'Redefined public method';
}
}
const test = new Child('test');
test.getData(); // => 'Data: test'
test.publicMethod(); // => 'Redefined public method'
test.data; // => 'test'
Клас В JavaScript використовується прототипне успадкування. В С++ конструктори при успадкуванні викликаються почергово від першого предка до останнього нащадка, а деструктори навпаки — від останнього нащадка до першого предка. class First
{
public:
First() { cout << ">>First constructor" << endl; }
~First() { cout << ">>First destructor" << endl; }
};
class Second: public First
{
public:
Second() { cout << ">Second constructor" << endl; }
~Second() { cout << ">Second destructor" << endl; }
};
class Third: public Second
{
public:
Third() { cout << "Third constructor" << endl; }
~Third() { cout << "Third destructor" << endl; }
};
// виконання коду
Third *th = new Third();
delete th;
// результат виведення
/*
>>First constructor
>Second constructor
Third constructor
Third destructor
>Second destructor
>>First destructor
*/
Див. також
Примітки
|
Portal di Ensiklopedia Dunia