ООП в TypeScript
Отличие процедурного подхода от объектно-ориентированного.
Процедурный подход подразумевал под собой обычно написание функции и определение того, когда функция будет вызваться и с какими параметрами
Однако использовать процедурный подход в программировании было тяжело, так как расширять такое приложение было крайне трудозатратным
Классы. Объекты. Свойства. Методы. Конструктор.
Класс - это описание характеристик будущего объекта. Объект - это конкретный экземпляр класса, где характеристики уже имеют свои значения
Конкретно характеристики в контексте ООП - это свойства, а его функции - это методы
Мы имеем конкретный объект Rectangle. У него есть два свойства: высота и ширина. Объектам обычно прописывается конструктор, который принимает в себя начальные значения для свойств класса. Так же у объекта есть свои методы, которые он может исполнить. Мы можем создать столько инстанцев класса, сколько нам нужно. Так же мы можем добавить нужное нам количество методов и свойств. Однако нужно помнить, что мы должны создавать объекты под конкретные задачи без лишних методов и свойств не характерных для данного объекта. this - это всегда обращение к данному объекту
В ООП главными парадигмами являются данные три:
Инкапсуляция и сокрытие. Модификаторы доступа.
Инкапсуляция - это парадигма, которая говорит нам, что данные и функции могут быть связаны, а данные внутри класса будут недоступны для изменений вне класса.
Как пример можно привести человека. Мы знаем, что он имеет публичнодоступные и известные методы и свойства (имеет имя, умеет ходить). Но так же он имеет приватные данные, которые мы можем получить только через метод (связь данных и функций - получить номер паспорта через просьбу достать паспорт) либо, которые мы передали в конструктор при создании и забыли про них. Так же мы не имеем доступа к внутренним методам мышления, переваривания пищи и остальных физиологических функций (кроме врачебных вмешательств - рефлексии)
Модификатор - это определённый элемент, который определяет доступность свойства или метода изнутри класса. Конкретно private - ограничивает область доступности данных полей внутри класса, а public делает поля доступными для просмотра и изменения и вне класса.
Приватные свойства по соглашению в ТС начинают с ” _ ” Для получения или изменения приватного свойства обычно используют геттеры (получение значения) и сеттеры (помещение значения). Если у нас будет отсутствовать сеттер, то свойство изменить мы не сможем (свойство автоматически станет readonly), если будет отсутствовать геттер, то получить значение свойства у нас так же не получится
Так же стоит упомянуть, что по умолчанию в ТС присваивается всем полям публичный модификатор. Хорошей практикой является явное указание публичных/приватных модификаторов
Так же ещё один пример. Нам не стоит редактировать таблицу базы данных напрямую. Вместо этого будет правильным решением сделать отдельный метод для заполнения таблицы нашей БД Если нам потребуется очистить нашу БД, то так же лучше создать отдельный метод для этих действий
Наследование.
Наследование подразумевает под собой переиспользование кода за счёт передачи его дочернему элементу.
То есть, например, мы имеем человека → этот человек может быть работником, а уже работника можно конкретизировать до программиста
Пример экстенда класса от других классов (персона - рабочий)
Ну и расширяемся дальше по прямой. Через метод супер вызывается конструктор родителя и уже в него передаются аргументы
И вот наследование методов от родительского
Полиморфизм. Параметрический и ad-hoc
Полиморфизм - "один интерфейс - множество реализаций".
Выделяют два вида полиморфизма: -Параметрический (истинный) -Ad-hoc (мнимый)
Ad-hoc. Представляет из себя перегрузку методов, когда мы передаём аргументы разных типов данных. Такой способ не работает в ТС
Параметрический полиморфизм. Уже он представляет из себя переопределение унаследованных методов на те, которые подойдут определённому объекту класса
Тут показана реализация перебора массива персон (который имеет тип массива родительского класса) и вызов у каждого из них метода приветствия
Агрегация и композиция.
Чуть выше было рассмотрено взаимодействие классов через наследование
Композиция подразумевает под собой то, что объекты не могут существовать отдельно друг без друга (автомобиль не может существовать отдельно от двигателя и колёс, так как он из них состоит)
Агрегация подразумевает под собой то, что объект может существовать отдельно от другого объекта. Ёлочка-освежитель может отдельно существовать без машины (находиться дома, например) и машина может иметь в себе не ёлочку, а качающегося дельфинчика
И вот реальный пример композиции. Машина не может существовать без колёс и двигателя (обязательно в конструкторе нужно их создать)
И вот пример агрегации. Мы отдельно создали класс освежителя и добавили его в качестве аргумента конструктора. Стоит сказать, что при удалении машины, освежитель останется и будет жить своей жизнью. А уже колёса и двигатель исчезнут вслед за машиной.
И свою жизнь освежитель сможет пролежать в квартире
Интерфейсы и абстрактные классы.
Интерфейсы представляют собой абстрактное представление функционала будущего объекта (например, класса). Можно представить, что это оглавление, которое описывает, что должно присутствовать в объекте.
Абстрактный класс - это уже конкретная схема класса со всеми методами, тем, что они должны возвращать и даже с логикой, которую можно передать через наследование
- Интерфейсы позволяют эффективно проектировать систему.
- Так же это представление работы полиморфизма.
- Создать класс из интерфейса нельзя - можно только имплементировать существующий класс
И вот пример создания класса из интерфейса (тут очень сильно помогает alt+insert, который сам реализует все методы интерфейсов)
Однако если нам нужно будет из интерфейса репозитория сделать репозиторий юзера, то нам нужно будет немного дополнить интерфейс и уточнить его под юзера (хотя интерфейс мы можем создать для многих объектов). Для исправления ситуации в интерфейс мы можем передать дженерик (обобщённый/динамический тип)
И теперь мы можем использовать репозиторий и для реализации репозитория машин: класс Car, класс CarRepository, интерфейс Repository
Внедрение зависимостей. Dependency injection
На всех тех принципах, что были озвучены выше, строится огромное количество паттернов проектирования
И вот пример реализации паттерна “Внедрение зависимостей”. У нас есть два слоя реализации приложения: работа с базой данных и бизнес-логика. Первый слой работает с двумя разными репозиториями БД (реляционная и нереляционная). В идеале, нам нужно, чтобы 2ой слой не знал, с какой БД мы работаем и в нём нам нужно будет реализовать имплементацию
И вот мы реализовали логику, когда мы в зависимости от разных аргументов получаем разную реализацию. Получаем мы разную реализацию за счёт того, что передаём не какой-то объект, а интерфейс. А из этого интерфейса идут две других реализации работы с БД, что и позволяет нам передать в один класс две разных логики (двух разных БД) Реализован опять же полиморфизм, но с использованием интерфейса
Паттерн singleton
Паттерн Singleton заставляет нас от одного класса создавать только один объект. Делать более одного экземпляра класса (а равно и подключения) - нельзя
Вот в примере, у нас создаются две разных БД. Внутри них генерируется уникальный идентификатор - их url. Это два разных поключения
Однако мы можем ограничить создание новых объектов. Можно сделать статичное свойство (то есть оно будет доступно без инициализации класса), в котором будет находиться значение нашего класса и этот инстанс будет из себя представлять всегда одинаковое статичное значение (= this). Это гарантирует постоянную одинаковость определённого значения класса Базы Данных
И теперь при каждой инициализации нашего класса, мы будем получать одно и то же значение определённого свойства (оно будет неизменным)