Урок 43: Продвинутые концепции ООП
Объектно-ориентированное программирование (ООП) — это парадигма программирования, основанная на концепции объектов. В этом уроке мы рассмотрим продвинутые концепции ООП, такие как наследование, полиморфизм, абстракция и инкапсуляция.
Наследование и полиморфизм
Наследование позволяет одному классу (подклассу) наследовать свойства и методы другого класса (суперкласса). Это позволяет создавать иерархии классов и повторно использовать код.
Полиморфизм позволяет объектам разных классов быть использованными через одинаковый интерфейс. Это означает, что один и тот же метод может вести себя по-разному в зависимости от объекта, который его вызывает.
Пример наследования и полиморфизма
Рассмотрим пример классов животных с наследованием и полиморфизмом:
// Суперкласс Animal
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} издает звук.`);
}
}
// Подкласс Dog, наследующий от Animal
class Dog extends Animal {
speak() {
console.log(`${this.name} лает.`);
}
}
// Подкласс Cat, наследующий от Animal
class Cat extends Animal {
speak() {
console.log(`${this.name} мяукает.`);
}
}
const animals = [new Dog('Рекс'), new Cat('Мурка')];
animals.forEach(animal => animal.speak());
// Рекс лает.
// Мурка мяукает.
В этом примере классы Dog
и Cat
наследуют от класса Animal
и переопределяют метод speak
. Полиморфизм позволяет вызывать метод speak
для объектов разных классов через один интерфейс.
Абстракция и инкапсуляция
Абстракция позволяет скрыть сложность системы и показать только ее существенные части. Это достигается путем создания абстрактных классов и интерфейсов.
Инкапсуляция — это механизм, который объединяет данные и методы, работающие с этими данными, в одном классе и скрывает детали реализации от внешнего мира. Это помогает защитить данные от некорректного использования.
Пример абстракции и инкапсуляции
Рассмотрим пример с использованием абстракции и инкапсуляции:
// Абстрактный класс Shape
class Shape {
constructor(color) {
if (this.constructor === Shape) {
throw new Error('Абстрактный класс не может быть инстанциирован.');
}
this.color = color;
}
draw() {
throw new Error('Метод "draw" должен быть реализован.');
}
}
// Подкласс Circle, наследующий от Shape
class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}
draw() {
console.log(`Рисуем круг радиусом ${this.radius} и цветом ${this.color}.`);
}
}
// Подкласс Square, наследующий от Shape
class Square extends Shape {
constructor(color, side) {
super(color);
this.side = side;
}
draw() {
console.log(`Рисуем квадрат со стороной ${this.side} и цветом ${this.color}.`);
}
}
const shapes = [new Circle('красный', 5), new Square('синий', 10)];
shapes.forEach(shape => shape.draw());
// Рисуем круг радиусом 5 и цветом красный.
// Рисуем квадрат со стороной 10 и цветом синий.
В этом примере класс Shape
является абстрактным и не может быть инстанциирован. Классы Circle
и Square
наследуют от него и реализуют метод draw
. Это демонстрирует абстракцию и инкапсуляцию данных.
Упражнения
Упражнение 1: Наследование и полиморфизм
Создайте классы Car
и Truck
, которые наследуют от класса Vehicle
. В классе Vehicle
должен быть метод drive
, который переопределяется в классах Car
и Truck
. Создайте несколько объектов и вызовите метод drive
для каждого из них.
Решение:
// Суперкласс Vehicle
class Vehicle {
constructor(type) {
this.type = type;
}
drive() {
console.log(`${this.type} едет.`);
}
}
// Подкласс Car, наследующий от Vehicle
class Car extends Vehicle {
drive() {
console.log(`Машина едет быстро.`);
}
}
// Подкласс Truck, наследующий от Vehicle
class Truck extends Vehicle {
drive() {
console.log(`Грузовик едет медленно.`);
}
}
const vehicles = [new Car('Машина'), new Truck('Грузовик')];
vehicles.forEach(vehicle => vehicle.drive());
// Машина едет быстро.
// Грузовик едет медленно.
Объяснение: Мы создали классы Car
и Truck
, которые наследуют от класса Vehicle
и переопределяют метод drive
.
Упражнение 2: Абстракция и инкапсуляция
Создайте абстрактный класс Employee
с методами work
и getSalary
. Создайте подклассы Developer
и Manager
, которые наследуют от Employee
и реализуют методы work
и getSalary
. Создайте несколько объектов и вызовите методы для каждого из них.
Решение:
// Абстрактный класс Employee
class Employee {
constructor(name, salary) {
if (this.constructor === Employee) {
throw new Error('Абстрактный класс не может быть инстанциирован.');
}
this.name = name;
this.salary = salary;
}
work() {
throw new Error('Метод "work" должен быть реализован.');
}
getSalary() {
return this.salary;
}
}
// Подкласс Developer, наследующий от Employee
class Developer extends Employee {
work() {
console.log(`${this.name} пишет код.`);
}
}
// Подкласс Manager, наследующий от Employee
class Manager extends Employee {
work() {
console.log(`${this.name} управляет командой.`);
}
}
const employees = [new Developer('Алиса', 5000), new Manager('Боб', 7000)];
employees.forEach(employee => {
employee.work();
console.log(`Зарплата: ${employee.getSalary()}`);
});
// Алиса пишет код.
// Зарплата: 5000
// Боб управляет командой.
// Зарплата: 7000
Объяснение: Мы создали абстрактный класс Employee
и подклассы Developer
и Manager
, которые реализуют методы work
и getSalary
.