Разные компании используют различные принципы написания кода, что приводит к трудностям при работе с чужой кодовой базой. Роберт Мартин (Дядя Боб) сформулировал пять принципов объектно-ориентированного программирования, известных как SOLID. Это аббревиатура, каждая буква которой обозначает отдельный принцип.
Основы ООП
Главная единица в объектно-ориентированном программировании (ООП) – это класс. Класс – это своего рода чертёж для создания объектов. Например, класс User может быть использован для создания объектов, представляющих пользователей. Класс содержит свойства (например, логин, электронную почту, пароль) и методы (например, войти_в_учетную_запись). Классы также поддерживают наследование: от класса User можно создать классы Admin и Subscriber, наследующие свойства и методы родительского класса и добавляющие свои собственные.
Принципы SOLID
Пять принципов SOLID:
S — Принцип Единственной Ответственности (Single Responsibility Principle)
«Один класс решает одну задачу.»
Каждый класс должен выполнять только одну чётко определённую задачу. Для этого общую задачу нужно разложить на подзадачи, каждая из которых реализуется в отдельном классе.
Пример: Неправильно создавать класс Employee, содержащий методы для расчёта зарплаты (payroll) и генерации отчёта (generate_report). Правильнее разделить функциональность на два класса: Employee (расчёт зарплаты) и Reports (генерация отчётов).
O — Принцип Открытости/Закрытости (Open/Closed Principle)
«Программные сущности должны быть открыты для расширения, но закрыты для модификации.»
Классы, модули и функции должны быть спроектированы так, чтобы их можно было расширять без изменения существующего кода. Это снижает риск поломки существующего функционала при добавлении новых возможностей. Для достижения этого используются абстракции, интерфейсы и наследование.
Пример: Система обработки заказов с различными способами оплаты. Неправильно создавать один класс Order с условными операторами для обработки разных типов платежей. Правильнее создать родительский класс PaymentProcessor и дочерние классы для каждого типа платежа (CashPaymentProcessor, CreditCardPaymentProcessor, MobilePaymentProcessor), наследующие метод process_payment. Добавление нового способа оплаты сводится к созданию нового дочернего класса.
L — Принцип Подстановки Лисков (Liskov Substitution Principle)
«Производные классы должны заменять свои базовые классы.»
Дочерние классы должны быть взаимозаменяемыми с родительскими классами без изменения поведения программы. Абстрактные классы помогают в этом.
Пример: Неправильный пример создания геометрического калькулятора с классами Figure, Square и Rectangle, где метод get_area переопределяется в дочерних классах. Правильный подход – использование абстрактного класса Figure с абстрактным методом get_area, который реализуется в дочерних классах без изменения логики родительского класса.
I — Принцип Разделения Интерфейса (Interface Segregation Principle)
«Клиенты не должны зависеть от интерфейсов, которые они не используют».
Необходимо создавать небольшие и узконаправленные интерфейсы, избегая больших интерфейсов с множеством ненужных методов.
Пример: Неправильно создавать один большой интерфейс Document с методами открыть, сохранить и закрыть. Правильнее разделить его на три отдельных интерфейса: DocumentOpener, DocumentSaver, DocumentCloser, которые можно использовать по отдельности в разных классах.
D — Принцип Инверсии Зависимостей (Dependency Inversion Principle)
«Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей, но детали должны зависеть от абстракций.»
Зависимости между компонентами программы должны строиться на основе абстракций (интерфейсов и абстрактных классов). Это позволяет легко заменять и модифицировать компоненты без нарушения работы всей системы. Полезные паттерны для реализации этого принципа – Внедрение Зависимостей и Инверсия Управления.
Пример: Неправильно создавать класс StudentsService, напрямую зависящий от класса StudentDataBase. Правильнее передавать зависимость в StudentsService через конструктор, что позволяет легко заменить реализацию работы с базой данных.
Принципы SOLID помогают создавать чистый, понятный и легко поддерживаемый объектно-ориентированный код. Однако, у SOLID есть и минусы: сложность внедрения, увеличение времени разработки, возможность переусердствовать с абстракциями, а также не универсальность применимости ко всем проектам. Важно применять принципы SOLID осознанно, выбирая наиболее подходящие под конкретные задачи.