Backend Typescript 1.0.0 Help

Объектно-ориентированное программирование

Что такое ООП и зачем оно нужно

Объектно-ориентированное программирование — это подход к разработке, в котором программа строится из взаимодействующих объектов. Каждый объект объединяет данные (состояние) и процедуры (поведение) так, чтобы их можно было воспринимать как элемент реального мира: кнопку интерфейса, банковский счёт, файл, фигуру на плоскости.

  • Модель мира: мы описываем сущности и их связи через классы и объекты.

  • Управление сложностью: скрываем внутренности (инкапсулируем), даём чёткие интерфейсы.

  • Повторное использование: наследование и композиция помогают переиспользовать код без копирования.

  • Гибкость: за счёт полиморфизма и интерфейсов код легче расширять.

Базовые понятия

Класс — это шаблон (чертёж) будущих объектов. Объект (экземпляр) — это конкретный представитель класса с конкретным состоянием. Поле хранит данные; метод описывает поведение; конструктор подготавливает объект к работе.

  • Класс определяет форму и поведение.

  • Экземпляр — это созданный объект по этому шаблону.

  • Сигнатура метода — имя + параметры (и их типы).

  • Интерфейс — контракт: «что умеет» без деталей «как».

Четыре столпа ООП

Инкапсуляция

Скрытие внутренностей и управление правилами работы с состоянием через методы. Это защищает инварианты класса и упрощает сопровождение.

Абстракция

Выделение существенного и «срезание» деталей. Через интерфейсы и абстрактные классы мы программируем «на уровне идей», а не реализаций.

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

Механизм, позволяющий одному классу «унаследовать» состояние и поведение другого. Важно отличать наследование интерфейса (обещание методов) и наследование реализации (перенятие кода).

Полиморфизм

Один интерфейс — много реализаций. Код работает с интерфейсом, а конкретный объект подставляется в рантайме (динамическая диспетчеризация).

Композиция против наследования и агрегации

Наследование описывает отношение «A — это B» (is-a). Композиция — отношение «A состоит из B» (has-a). Композиция менее хрупкая и чаще предпочтительна для повторного использования поведения.

Агрегация — это разновидность композиции, но с более «слабой» связью. В композиции части не могут существовать без целого (двигатель обычно не существует без автомобиля), а в агрегации объект может жить отдельно от «владельца». Например, университет содержит студентов, но студенты могут существовать и без конкретного университета.

  • «Круг — это Фигура» — разумное наследование.

  • «Автомобиль состоит из Двигателя» — это композиция, а не наследование.

  • «Университет содержит Студентов» — это агрегация: студенты могут учиться в разных местах или временно не принадлежать университету.

Примеры: Наследование, Композиция, Агрегация

Пример: Shape и Circle (наследование), Car и Engine (композиция), University и Student (агрегация).

// Наследование struct Shape { virtual double area() const = 0; }; struct Circle : Shape { double r; Circle(double r) : r(r) {} double area() const override { return 3.14 * r * r; } }; // Композиция struct Engine { int rpm = 0; void rev(int delta) { rpm += delta; } }; struct Car { Engine engine; // часть объекта void accelerate() { engine.rev(1000); } }; // Агрегация struct Student { std::string name; }; struct University { std::vector<Student*> students; // могут жить отдельно void add(Student* s) { students.push_back(s); } };
// Наследование abstract class Shape { abstract double area(); } class Circle extends Shape { double r; Circle(double r) { this.r = r; } double area() { return Math.PI * r * r; } } // Композиция class Engine { private int rpm = 0; void rev(int delta) { rpm += delta; } int getRpm() { return rpm; } } class Car { private final Engine engine = new Engine(); void accelerate() { engine.rev(1000); } } // Агрегация class Student { String name; } class University { private List<Student> students = new ArrayList<>(); void add(Student s) { students.add(s); } }
// Наследование class Shape { area() { throw "abstract"; } } class Circle extends Shape { constructor(r) { super(); this.r = r; } area() { return Math.PI * this.r * this.r; } } // Композиция class Engine { constructor() { this.rpm = 0; } rev(delta) { this.rpm += delta; } } class Car { constructor() { this.engine = new Engine(); } accelerate() { this.engine.rev(1000); } } // Агрегация class Student { constructor(name) { this.name = name; } } class University { constructor() { this.students = []; } add(student) { this.students.push(student); } }
// Наследование abstract class Shape { abstract area(): number; } class Circle extends Shape { constructor(private r: number) { super(); } area(): number { return Math.PI * this.r * this.r; } } // Композиция class Engine { private rpm = 0; rev(delta: number) { this.rpm += delta; } } class Car { private engine = new Engine(); accelerate() { this.engine.rev(1000); } } // Агрегация class Student { constructor(public name: string) { } } class University { private students: Student[] = []; add(s: Student) { this.students.push(s); } }
package main import "fmt" // Наследование нет, используем интерфейсы type Shape interface { Area() float64 } type Circle struct { R float64 } func (c Circle) Area() float64 { return 3.14 * c.R * c.R } // Композиция type Engine struct { Rpm int } func (e *Engine) Rev(delta int) { e.Rpm += delta } type Car struct { Engine } // встраивание (композиция) func (c *Car) Accelerate() { c.Rev(1000) } // Агрегация type Student struct { Name string } type University struct { Students []*Student } func (u *University) Add(s *Student) { u.Students = append(u.Students, s) } func main() { s := &Student{"Bob"} u := &University{} u.Add(s) fmt.Println(u.Students[0].Name) // Bob }
' Наследование в VBA нет, только интерфейсы через Implements ' Композиция ' Class Module: Engine Public Rpm As Integer Public Sub Rev(delta As Integer) Rpm = Rpm + delta End Sub ' Class Module: Car Private eng As Engine Private Sub Class_Initialize() Set eng = New Engine End Sub Public Sub Accelerate() eng.Rev 1000 End Sub ' Агрегация ' Class Module: Student Public Name As String ' Class Module: University Private students As Collection Private Sub Class_Initialize() Set students = New Collection End Sub Public Sub Add(s As Student) students.Add s End Sub

Интерфейсы, абстрактные классы, контракты

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

Жизненный цикл объекта

  • Создание: конструктор/фабрика инициализируют состояние.

  • Использование: объект обеспечивает инварианты через методы.

  • Завершение: в C++ — деструктор, в Java/JS/TS — сборщик мусора.

ООП в разных языках: единый пример (Фигура → Площадь)

Ниже — одинаковая модель: есть «фигура» и конкретная реализация «прямоугольник». Мы вызываем метод через абстракцию и получаем полиморфное поведение.

\#include \<iostream\> \#include \<memory\> struct Shape { virtual double area() const = 0; virtual \~Shape() = default; }; struct Rect : Shape { double w, h; Rect(double w, double h) : w(w), h(h) {} double area() const override { return w \* h; } }; int main() { std::unique\_ptr\<Shape\> s = std::make\_unique\<Rect\>(3, 4); std::cout \<\< s-\>area() \<\< "\n"; // 12 }
interface Shape { double area(); } class Rect implements Shape { double w, h; Rect(double w, double h) { this.w = w; this.h = h; } public double area() { return w \* h; } } public class Main { public static void main(String\[] args) { Shape s = new Rect(3, 4); System.out.println(s.area()); // 12 } }
class Rect { constructor(w, h) { this.w = w; this.h = h; } area() { return this.w * this.h; } } const s = new Rect(3, 4); console.log(s.area()); // 12
interface Shape { area(): number; } class Rect implements Shape { constructor(public w: number, public h: number) { } area(): number { return this.w * this.h; } } const s: Shape = new Rect(3, 4); console.log(s.area()); // 12
package main import ( "fmt" ) type Shape interface { Area() float64 } type Rect struct { W, H float64 } func (r Rect) Area() float64 { return r.W \* r.H } func main() { var s Shape = Rect{3, 4} fmt.Println(s.Area()) // 12 }
' Class Module: Rect ' Public W As Double ' Public H As Double ' Public Function Area() As Double ' Area = W \* H ' End Function ' Module Sub Demo() Dim s As Rect Set s = New Rect s.W = 3 s.H = 4 Debug.Print s.Area ' 12 End Sub

Практические рекомендации

  • Названия: классы — существительные, методы — глаголы. Из названия понятна ответственность.

  • Программируйте против абстракций: зависимости — через интерфейсы, передавайте их снаружи.

  • Не злоупотребляйте наследованием: если сомневаетесь — берите композицию.

  • Инварианты: защищайте корректность состояния внутри класса (проверки в методах, не только при создании).

Частые подводные камни

  • Хрупкий базовый класс: изменения в родителе ломают наследников. Смягчайте через композицию и тесты контрактов.

  • Алмаз наследования (C++): множественное наследование порождает неоднозначность. Решение — виртуальное наследование или отказ от него.

  • Утечки инкапсуляции: возврат «сырого» изменяемого состояния наружу (getItems() отдаёт изменяемый список). Возвращайте копии или иммутабельные представления.

  • Сложные конструкторы: тяжёлые операции при создании усложняют ошибки/тесты. Используйте фабрики/ленивую инициализацию.

Last modified: 01 October 2025