Files
obsidian/Собеседование .Net.md
2026-01-20 16:11:38 +03:00

111 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Объектно-ориентирование программирование
### Абстракция
Абстрагирование — это способ выделить набор наиболее важных атрибутов и методов и исключить незначимые. Соответственно, абстракция — это использование всех таких характеристик для описания объекта. Важно представить объект минимальным набором полей и методов без ущерба для решаемой задачи.
Пример: объекту класса «программист» вряд ли понадобятся свойства «умение готовить еду» или «любимый цвет». Они не влияют на его особенности как программиста. А вот «основной язык программирования» и «рабочие навыки» — важные свойства, без которых программиста не опишешь.
Набор атрибутов и методов, доступный извне, работает как интерфейс для доступа к объекту. Через них к нему могут обращаться другие структуры данных, причем им не обязательно знать, как именно объект устроен внутри.
### Инкапсуляция
Каждый объект — независимая структура. Все, что ему нужно для работы, уже есть у него внутри. Если он пользуется какой-то переменной, она будет описана в теле объекта, а не снаружи в коде. Это делает объекты более гибкими. Даже если внешний код перепишут, логика работы не изменится.
Инкапсуляция помогает с легкостью управлять кодом. Выше мы сказали, что для обращения к объекту не нужно понимать, как работают его методы. Начальнику разработчика Ивана не обязательно знать, как именно он программирует: главное — чтобы выполнялись поставленные задачи.
Внутреннее устройство одного объекта закрыто от других: извне «видны» только значения атрибутов и результаты выполнения методов.
### Наследование
Можно создавать классы и объекты, которые похожи друг на друга, но немного отличаются — имеют дополнительные атрибуты и методы. Более общее понятие в таком случае становится «родителем», а более специфичное и подробное — «наследником».
Упомянутый программист Иван — это человек. Но «человек» — более общее определение, которое не описывает свойства, важные именно для программиста. Можно сказать, что класс «программист» унаследован от класса «человек»: программист тоже является человеком, но у него есть дополнительные свойства.
В таком случае разработчик Иван будет и человеком, и программистом одновременно. У него будут наборы свойств от обоих классов.
У одного «родителя» может быть несколько дочерних структур. Например, от «человека» можно наследовать не только «программиста», но и «директора».
Наследование позволяет реализовывать сложные схемы с четкой иерархией «от общего к частному». Это облегчает понимание и масштабирование кода. Не нужно много раз переписывать в разных объектах одни и те же свойства. Достаточно унаследовать эти объекты от одного «родителя», и «родительские» свойства применятся автоматически.
### Полиморфизм
Одинаковые методы разных объектов могут выполнять задачи разными способами. Например, у «человека» есть метод «работать». У «программиста» реализация этого метода будет означать написание кода, а у «директора» — рассмотрение управленческих вопросов. Но глобально и то, и другое будет работой.
Тут важны единый подход и договоренности между специалистами. Если метод называется 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** (Инверсия управления), чтобы передавать зависимости извне вместо того, чтобы создавать их внутри компонентов.
**Внедрение зависимостей**: объект не создает свои зависимости самостоятельно, они предоставляются ему извне, например через конструктор, методы или свойства.
**Инверсия управления**: управление частью приложения переносится на внешний фреймворк или контейнер, управляющий жизненным циклом объектов.
**Задача**
Нужно разработать приложение для работы с базой данных студентов. Каждый студент имеет имя, возраст и список предметов, которые он изучает.