111 lines
13 KiB
Markdown
111 lines
13 KiB
Markdown
|
||
## Объектно-ориентирование программирование
|
||
|
||
### Абстракция
|
||
|
||
Абстрагирование — это способ выделить набор наиболее важных атрибутов и методов и исключить незначимые. Соответственно, абстракция — это использование всех таких характеристик для описания объекта. Важно представить объект минимальным набором полей и методов без ущерба для решаемой задачи.
|
||
|
||
Пример: объекту класса «программист» вряд ли понадобятся свойства «умение готовить еду» или «любимый цвет». Они не влияют на его особенности как программиста. А вот «основной язык программирования» и «рабочие навыки» — важные свойства, без которых программиста не опишешь.
|
||
|
||
Набор атрибутов и методов, доступный извне, работает как интерфейс для доступа к объекту. Через них к нему могут обращаться другие структуры данных, причем им не обязательно знать, как именно объект устроен внутри.
|
||
|
||
### Инкапсуляция
|
||
|
||
Каждый объект — независимая структура. Все, что ему нужно для работы, уже есть у него внутри. Если он пользуется какой-то переменной, она будет описана в теле объекта, а не снаружи в коде. Это делает объекты более гибкими. Даже если внешний код перепишут, логика работы не изменится.
|
||
|
||
Инкапсуляция помогает с легкостью управлять кодом. Выше мы сказали, что для обращения к объекту не нужно понимать, как работают его методы. Начальнику разработчика Ивана не обязательно знать, как именно он программирует: главное — чтобы выполнялись поставленные задачи.
|
||
|
||
Внутреннее устройство одного объекта закрыто от других: извне «видны» только значения атрибутов и результаты выполнения методов.
|
||
|
||
### Наследование
|
||
|
||
Можно создавать классы и объекты, которые похожи друг на друга, но немного отличаются — имеют дополнительные атрибуты и методы. Более общее понятие в таком случае становится «родителем», а более специфичное и подробное — «наследником».
|
||
|
||
Упомянутый программист Иван — это человек. Но «человек» — более общее определение, которое не описывает свойства, важные именно для программиста. Можно сказать, что класс «программист» унаследован от класса «человек»: программист тоже является человеком, но у него есть дополнительные свойства.
|
||
|
||
В таком случае разработчик Иван будет и человеком, и программистом одновременно. У него будут наборы свойств от обоих классов.
|
||
|
||
У одного «родителя» может быть несколько дочерних структур. Например, от «человека» можно наследовать не только «программиста», но и «директора».
|
||
|
||
Наследование позволяет реализовывать сложные схемы с четкой иерархией «от общего к частному». Это облегчает понимание и масштабирование кода. Не нужно много раз переписывать в разных объектах одни и те же свойства. Достаточно унаследовать эти объекты от одного «родителя», и «родительские» свойства применятся автоматически.
|
||
|
||
### Полиморфизм
|
||
|
||
Одинаковые методы разных объектов могут выполнять задачи разными способами. Например, у «человека» есть метод «работать». У «программиста» реализация этого метода будет означать написание кода, а у «директора» — рассмотрение управленческих вопросов. Но глобально и то, и другое будет работой.
|
||
|
||
Тут важны единый подход и договоренности между специалистами. Если метод называется delete, то он должен что-то удалять. Как именно — зависит от объекта, но заниматься такой метод должен именно удалением. Более того: если оговорено, что «удаляющий» метод называется delete, то не нужно для какого-то объекта называть его remove или иначе. Это вносит путаницу в код.
|
||
## SOLID
|
||
|
||
### Single Responsibility Principle (Принцип единственной ответственности)
|
||
|
||
Это значит, что каждый класс должен выполнять только **одну** четко определенную функцию. Если он решает более одной задачи, это может привести к сложностям в поддержке и расширении кода.
|
||
|
||
**Как следовать принципу SRP**
|
||
|
||
Сначала общую задачу нужно декомпозировать на несколько подзадач. Затем каждую подзадачу реализовать в отдельном классе.
|
||
|
||
### Open/Closed Principle (Принцип открытости/закрытости)
|
||
|
||
**Программные сущности (классы, модули, функции и т.д.) должны быть открыты для расширения, но закрыты для модификации.**
|
||
|
||
Это значит, что программные компоненты нужно спроектировать таким образом, чтобы их можно было расширять новым функционалом, не меняя исходный код. Изменения при добавлении нового кода могут сломать уже работающую логику.
|
||
|
||
**Как следовать принципу OCP**
|
||
|
||
В этом помогут абстракции, интерфейсы и наследование. Это позволит добавить новый функционал через расширение, а не модификацию существующего кода.
|
||
|
||
**Задача**
|
||
|
||
У ресторана есть система управления заказами. Нужно сделать так, чтобы она поддерживала различные виды платежей: наличные, кредитные карты и мобильные платежи.
|
||
|
||
### Liskov Substitution Principle (Принцип подстановки Барбары Лисков)
|
||
|
||
**Производные классы должны заменять свои базовые классы.**
|
||
|
||
Это значит, что объекты базовых классов должны быть заменяемы объектами производных классов без изменения ожидаемого поведения программы.
|
||
|
||
**_Базовый класс_** — это класс, от которого производные классы наследуют свойства и методы.
|
||
|
||
**Как следовать принципу LSP**
|
||
|
||
Производный класс должен сохранять все свойства базового класса и не изменять их семантику. Нужно добиться того, чтобы объекты производных классов могли безопасно заменять друг друга и базовый класс.
|
||
|
||
**Задача**
|
||
|
||
Предположим, что есть система для работы с геометрическими фигурами, в которой есть базовый класс **Shape** и производные классы **Circle** и **Square**.
|
||
|
||
### Interface Segregation Principle (Принцип разделения интерфейса)
|
||
|
||
**Клиенты не должны зависеть от интерфейсов, которые они не используют.**
|
||
|
||
Это значит, что нужно создавать только небольшие и узконаправленные интерфейсы, не перегруженные ненужными методами.
|
||
|
||
**Как следовать принципу ISP**
|
||
|
||
Каждый интерфейс должен существовать для определенных задач и содержать только те методы, которые эти задачи решают.
|
||
|
||
**Задача**
|
||
|
||
Предположим, у нас есть система управления документами с интерфейсом **Document,** содержащим методы для работы с документами, такие как **open()**, **save()** и **close().** Однако одним клиентам требуется только возможность открывать и закрывать документы, а другим — только сохранять и закрывать их.
|
||
|
||
### Dependency Inversion Principle (Принцип инверсии зависимостей)
|
||
|
||
**Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей, но детали должны зависеть от абстракций.**
|
||
|
||
Это значит, что код должен быть организован таким образом, чтобы зависимости между компонентами программы были основаны на абстракциях, а не на конкретных реализациях. Таким образом, компоненты легко заменить или изменить без воздействия на другие части системы.
|
||
|
||
**Как следовать принципу DIP**
|
||
|
||
— Использовать интерфейсы или абстрактные классы для определения зависимостей между компонентами.
|
||
|
||
— Следовать остальным принципам SOLID, чтобы создавать хорошо структурированные и модульные системы.
|
||
|
||
— Применять шаблоны проектирования, такие как **Dependency Injection** (Внедрение зависимостей) или **Inversion of Control** (Инверсия управления), чтобы передавать зависимости извне вместо того, чтобы создавать их внутри компонентов.
|
||
|
||
**Внедрение зависимостей**: объект не создает свои зависимости самостоятельно, они предоставляются ему извне, например через конструктор, методы или свойства.
|
||
|
||
**Инверсия управления**: управление частью приложения переносится на внешний фреймворк или контейнер, управляющий жизненным циклом объектов.
|
||
|
||
**Задача**
|
||
|
||
Нужно разработать приложение для работы с базой данных студентов. Каждый студент имеет имя, возраст и список предметов, которые он изучает. |