Single responsibility (Принцип единственной ответственности)
Open–closed (Принцип открытости/закрытости)
Liskov substitution (Принцип подстановки Лисков)
Interface segregation (Принцип разделения интерфейса)
Dependency inversion (Принцип инверсии зависимостей)
В этой статье использовал материалы Ulbi TV
https://youtu.be/TxZwqVTaCmA
1. Парадигмы ООП
1.1. Инкапсуляция
1.2. Наследование
1.3. Полиморфизм
1.1. Инкапсуляция
Инкапсуляция — это механизм программирования, объединяющий вместе код и данные, которыми он манипулирует, исключая как вмешательство извне, так и не правильное использование данных. В объектно-ориентированном языке данные и код могут быть объединены в совершенно автономный черный ящик. Внутри такого ящика находятся все необходимые данные и код. Когда код и данные связываются вместе подобным образом, создается объект.
Другими словами. инкапсуляция - это заключение данных и функциональности в оболочку.
В объектно-ориентированном программировании в роли оболочки выступают классы: они не только собирают переменные и методы в одном месте, но и защищают их от вмешательства извне (сокрытие).
Методы позволяют контролировать обращение к данным и предотвратить их удаление или некорректное изменение.
1.2. Наследование
Наследование - это концепция объектно-ориентированного программирования, согласно которой абстрактный тип данных может наследовать данные и функциональность некоторого существующего типа, способствуя повторному использованию компонентов программного обеспечения.
Наследование является механизмом повторного использования кода (англ. code reuse) и способствует независимому расширению программного обеспечения через открытые классы (англ. public classes) и интерфейсы (англ. interfaces). Установка отношения наследования между классами порождает иерархию классов (англ. class hierarchy).
В объектно-ориентированном программировании, начиная с Simula 67, абстрактные типы данных называются классами.
Суперкласс (англ. super class), родительский класс (англ. parent class), предок, родитель или надкласс — класс, производящий наследование в подклассах, т. е. класс, от которого наследуются другие классы. Суперклассом может быть подкласс, базовый класс, абстрактный класс и интерфейс.
Подкласс (англ. subclass), производный класс (англ. derived class), дочерний класс (англ. child class), класс потомок, класс наследник или класс-реализатор — класс, наследуемый от суперкласса или интерфейса, т. е. класс определённый через наследование от другого класса или нескольких таких классов. Подклассом может быть суперкласс.
Базовый класс (англ. base class) — это класс, находящийся на вершине иерархии наследования классов и в основании дерева подклассов, т. е. не являющийся подклассом и не имеющий наследований от других суперклассов или интерфейсов. Базовым классом может быть абстрактный класс и интерфейс. Любой не базовый класс является подклассом.
Интерфейс (англ. interface) — это структура, определяющая чистый интерфейс класса, состоящий из абстрактных методов. Интерфейсы участвуют в иерархии наследований классов и интерфейсов.
Суперинтерфейс (англ. super interface) или интерфейс-предок — это аналог суперкласса в иерархии наследований, т. е. это интерфейс производящий наследование в подклассах и подинтерфейсах.
Интерфейс-потомок, интерфейс-наследник или производный интерфейс (англ. derived interface) — это аналог подкласса в иерархии наследований интерфейсов, т. е. это интерфейс наследуемый от одного или нескольких суперинтерфейсов.
Базовый интерфейс — это аналог базового класса в иерархии наследований интерфейсов, т. е. это интерфейс, находящийся на вершине иерархии наследования.
Иерархия наследования или иерархия классов — дерево, элементами которого являются классы и интерфейсы.
1.3. Полиморфизм
Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. И как следствие из предыдущего определения, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового.
Полиморфизм даёт возможность использовать одни и те же методы для объектов разных классов.
Неважно, как эти объекты устроены, — в ООП можно сказать самолёту и квадрокоптеру: «Лети», и они будут делать это как умеют: квадрокоптер закрутит лопастями, а самолёт начнёт разгон по взлётно-посадочной полосе. Грубо говоря, полиморфизм — это диспетчер в аэропорту. Ему неважно, какую топливную систему предусмотрел авиаконструктор и как работает система форсажа — он просто даёт команду: «Взлёт разрешаю». После этого на лайнере начинают происходить какие-то свои внутренние процессы, на которые диспетчер уже не влияет.
Преимуществом полиморфизма является то, что он помогает снижать сложность программ, разрешая использование одного и того же интерфейса для задания единого набора действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор языка программирования.
Немного теории
- Методы, которые требуют переопределения, называются абстрактными. Логично, что если класс содержит хотя бы один абстрактный метод, то он тоже является абстрактным.
- Очевидно, что обьект абстрактного класса невозможно создать, иначе он не был бы абстрактным.
- Производный класс имеет свойства и методы, принадлежащие базовому классу, и, кроме того, может иметь собственные методы и свойства.
- Метод, переопределяемый в производном классе, называется виртуальным. В базовом абстрактном классе об этом методе нет никакой информации.
- Суть абстрагирования в том, чтобы определять метод в том месте, где есть наиболее полная информация о том, как он должен работать.
2. Single responsibility (Принцип единственной ответственности)
1 класс = 1 задача
1 сущность = 1 задача
3. Open–closed (Принцип открытости/закрытости)
Сущности должны быть открыты для расширения и закрыты для изменения.
Изменять уже работающий и протестированный код - плохая практика, так как нужно будут делать регрессионное тестирование. То есть заново проверять все методы сущности, что ничего не сломалось.
Ниже на схеме показан неправильный подход. Нужно изменять сущности не в самих сущностях, а за счет композиции или наследования создавать новую сущность и реализовывать новый функционал уже там.
4. Liskov substitution (Принцип подстановки Лисков)
Сущности, используюущие родительский тип (класс), должны точно также работать и с дочерними типами (классами).
Другими словами, наследуемый класс должен дополнять, а не замещать, поведение базового класса.
Ниже на схеме наследуемые классы добавляют методы к родительскому классу. Красным показан неправильный подход, когда в наследуемом классе переопределяется метод родительского класса.
5. Interface segregation (Принцип разделения интерфейса)
Сущности не должны зависеть от методов, которые они не используют.
На схеме показан неправильный подход: классы второй и третий не используют часть методов родительского класса.
Ниже показан правильный подход. Нужно разработать три интерфейса и имплементировать их в созданные классы.
6. Dependency inversion (Принцип инверсии зависимостей)
Модули высокого уровня не должны зависеть от модулей более низкого уровня. Все они должны зависеть от абстракций. А абстракции, в тоже время, не должны зависеть от деталей.
Представим завод, на котором есть три верхние сущности: работники, станки, электричество. Сущность станки имеет сущности более низкого порядка. Работники имеют соответствующие компетенции для работы с этими станками. Электричество поставляется 380V.
Но сломался один станок (сломалась деталь) и его поменяли на более новый станок, который потребляет теперь уже 220V. Кроме того, нет ни одного работника умеющего на нем работать.
Получается, что изменение сущности низкого порядка (станок) привело к необходимости менять сущности верхнего порядка. А этого не должно быть в соответствии с рассматриваемым принципом.
Для решения этой проблемы должны быть сущности:
пульт управления (сущность - одинаковый интерфейс для всех станков), с которыми взаимодействуют работники,
трансформатор (сущность - на входе 380V, а на выходе оба вида напряжения).
ВСЁ ❗️😀