7.1 Основы наследования
Наследование — один из ключевых принципов объектно-ориентированного программирования (ООП), который позволяет создавать новые классы на основе уже существующих. В TypeScript наследование помогает организовать иерархию классов, что упрощает повторное использование кода, уменьшает дублирование и улучшает читаемость.
В TypeScript наследование позволяет одному классу (производному или дочернему классу) наследовать свойства и методы другого класса (базового или родительского класса). Это достигается с помощью ключевого слова extends.
Синтаксис наследования
Для создания производного класса, который наследует свойства и методы базового класса, используется следующий синтаксис:
class BaseClass {
// Свойства и методы базового класса
}
class DerivedClass extends BaseClass {
// Свойства и методы производного класса
}
Примеры наследования
Рассмотрим пример с базовым классом Animal и производным классом Dog:
"use strict";
class Animal {
constructor(name) {
this.name = name;
}
move(distance) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
let dog = new Dog('Rex');
dog.bark(); // Вывод: Woof! Woof!
dog.move(10); // Вывод: Rex moved 10 meters.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark(): void {
console.log('Woof! Woof!');
}
}
let dog = new Dog('Rex');
dog.bark(); // Вывод: Woof! Woof!
dog.move(10); // Вывод: Rex moved 10 meters.
Здесь класс Dog наследует свойства и методы класса Animal. Экземпляр класса Dog может использовать как метод bark, так и метод move, унаследованный от класса Animal.
7.2 Конструкторы в наследуемых классах
Производные классы могут иметь свои собственные конструкторы. В этом случае необходимо вызвать конструктор базового класса с помощью ключевого слова super.
Синтаксис использования super
Ключевое слово super используется для вызова конструктора и методов базового класса из производного класса.
Пример:
"use strict";
class Animal {
constructor(name) {
this.name = name;
}
move(distance) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Bird extends Animal {
constructor(name, wingSpan) {
super(name); // Вызов конструктора базового класса
this.wingSpan = wingSpan;
}
fly(distance) {
console.log(`${this.name} with wingspan of ${this.wingSpan} meters flew ${distance} meters.`);
}
}
let bird = new Bird('Eagle', 2);
bird.fly(100); // Вывод: Eagle with wingspan of 2 meters flew 100 meters.
bird.move(20); // Вывод: Eagle moved 20 meters.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Bird extends Animal {
wingSpan: number;
constructor(name: string, wingSpan: number) {
super(name); // Вызов конструктора базового класса
this.wingSpan = wingSpan;
}
fly(distance: number): void {
console.log(`${this.name} with wingspan of ${this.wingSpan} meters flew ${distance} meters.`);
}
}
let bird = new Bird('Eagle', 2);
bird.fly(100); // Вывод: Eagle with wingspan of 2 meters flew 100 meters.
bird.move(20); // Вывод: Eagle moved 20 meters.
Здесь у класса Bird есть собственный конструктор, который вызывает конструктор базового класса Animal с помощью super(name). Также добавляется новое свойство wingSpan и метод fly.
7.3 Переопределение методов
Производные классы могут переопределять методы базового класса для предоставления новой реализации. Для этого используется тот же синтаксис, что и при объявлении метода в базовом классе.
"use strict";
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log('Some generic animal sound');
}
}
class Cat extends Animal {
constructor(name) {
super(name);
}
makeSound() {
console.log('Meow! Meow!');
}
}
let animal = new Animal('Generic Animal');
animal.makeSound(); // Вывод: Some generic animal sound
let cat = new Cat('Whiskers');
cat.makeSound(); // Вывод: Meow! Meow!
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log('Some generic animal sound');
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
console.log('Meow! Meow!');
}
}
let animal = new Animal('Generic Animal');
animal.makeSound(); // Вывод: Some generic animal sound
let cat = new Cat('Whiskers');
cat.makeSound(); // Вывод: Meow! Meow!
В этом примере метод makeSound класса Cat переопределяет метод makeSound класса Animal, предоставляя собственную реализацию.
7.4 Доступ к методам базового класса
Производные классы могут вызывать методы базового класса с помощью ключевого слова super.
Пример:
"use strict";
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log('Some generic animal sound');
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
makeSound() {
super.makeSound(); // Вызов метода базового класса
console.log('Woof! Woof!');
}
}
let dog = new Dog('Rex');
dog.makeSound();
// Вывод:
// Some generic animal sound
// Woof! Woof!
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log('Some generic animal sound');
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
super.makeSound(); // Вызов метода базового класса
console.log('Woof! Woof!');
}
}
let dog = new Dog('Rex');
dog.makeSound();
// Вывод:
// Some generic animal sound
// Woof! Woof!
Здесь метод makeSound класса Dog вызывает метод makeSound базового класса Animal с помощью super.makeSound(), а затем выполняет собственную логику.
7.5 Примеры
Наследование активно используется в реальных проектах для моделирования иерархий объектов и их взаимодействий. Рассмотрим несколько примеров.
Пример 1: Система управления персоналом
"use strict";
class Employee {
constructor(name, position) {
this.name = name;
this.position = position;
}
work() {
console.log(`${this.name} is working as a ${this.position}.`);
}
}
class Manager extends Employee {
constructor(name, department) {
super(name, 'Manager');
this.department = department;
}
work() {
super.work();
console.log(`${this.name} is managing the ${this.department} department.`);
}
}
let manager = new Manager('Alice', 'Sales');
manager.work();
// Вывод:
// Alice is working as a Manager.
// Alice is managing the Sales department.
class Employee {
name: string;
position: string;
constructor(name: string, position: string) {
this.name = name;
this.position = position;
}
work(): void {
console.log(`${this.name} is working as a ${this.position}.`);
}
}
class Manager extends Employee {
department: string;
constructor(name: string, department: string) {
super(name, 'Manager');
this.department = department;
}
work(): void {
super.work();
console.log(`${this.name} is managing the ${this.department} department.`);
}
}
let manager = new Manager('Alice', 'Sales');
manager.work();
// Вывод:
// Alice is working as a Manager.
// Alice is managing the Sales department.
Здесь класс Manager наследует от класса Employee и переопределяет метод work, добавляя дополнительное поведение.
Пример 2: Моделирование транспортных средств
"use strict";
class Vehicle {
constructor(make, model) {
this.make = make;
this.model = model;
}
move() {
console.log(`The ${this.make} ${this.model} is moving.`);
}
}
class Car extends Vehicle {
constructor(make, model, numDoors) {
super(make, model);
this.numDoors = numDoors;
}
move() {
super.move();
console.log(`The car has ${this.numDoors} doors.`);
}
}
let car = new Car('Toyota', 'Camry', 4);
car.move();
// Вывод:
// The Toyota Camry is moving.
// The car has 4 doors.
class Vehicle {
make: string;
model: string;
constructor(make: string, model: string) {
this.make = make;
this.model = model;
}
move(): void {
console.log(`The ${this.make} ${this.model} is moving.`);
}
}
class Car extends Vehicle {
numDoors: number;
constructor(make: string, model: string, numDoors: number) {
super(make, model);
this.numDoors = numDoors;
}
move(): void {
super.move();
console.log(`The car has ${this.numDoors} doors.`);
}
}
let car = new Car('Toyota', 'Camry', 4);
car.move();
// Вывод:
// The Toyota Camry is moving.
// The car has 4 doors.
Здесь класс Car наследует от класса Vehicle и переопределяет метод move, добавляя дополнительную информацию о количестве дверей.
Преимущества наследования
- Повторное использование кода: наследование позволяет повторно использовать код базового класса, избегая дублирования и упрощая сопровождение.
- Расширяемость: производные классы могут добавлять новые свойства и методы или переопределять существующие, что упрощает расширение функциональности.
- Структурированность: наследование помогает организовать код в логически связанные иерархии, улучшая его читаемость и поддержку.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ