Принципы SOLID - Программирование на языке C++

SOLID - мнемонический акроним, введенный Майклом Фэзерсом (Michael Feathers) для первых пяти принципов, названных Робертом Мартином в начале 2000-х, которые означали пять основных принципов объектно-ориентированного программирования и проектирования. Эти принципы, когда применяются вместе, предназначены для повышения вероятности того, что программист создаст систему, которую будет легко поддерживать и расширять в течение долгого времени. Принципы SOLID -- это руководства, которые могут применяться во время работы над программным обеспечением для удаления "кода с запашком" предписывая программисту выполнять рефакторинг исходного кода, пока тот не станет разборчиво написанным и расширяемым. Это часть общей стратегии гибкой и адаптивной разработки.

Описание

S (single responsibility principle, принцип единственной обязанности) обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности.

Термин был введен Робертом С. Мартином в одноименной статье как часть его Принципов объектно-ориентированного проектирования, ставших популярными благодаря его книге Быстрая разработка программ. Принципы, примеры, практика. Мартин описал ее как основанную на принципе связности, сформулированномТомом ДеМарко в его книге Structured Analysis and Systems Specification.

Мартин определяет обязанность как причину изменения и заключает, что класс или модуль должны иметь одну и только одну причину измениться. Например, представьте себе модуль, который составляет и печатает отчет. Такой модуль может измениться по двум причинам. Во-первых, может измениться само содержимое отчета. Во-вторых, может измениться формат отчета. Оба этих фактора изменяют модуль по разным причинам: в одном случае изменение содержательное, а во втором -- косметическое. Принцип единственной обязанности говорит, что оба аспекта этой проблемы на самом деле являются двумя разными обязанностями, и в таком случае должны находиться в разных классах или модулях. Объединение двух сущностей, изменяющихся по разным причинам и в разное время, считается плохим проектным решением.

Причина, почему нужно сохранять направленность класса на единственную цель в том, что это делает класс более здоровым. Что касается приведенного выше примера, если произошло изменение в процессе составления отчета -- есть большая вероятность, что в негодность придет код, отвечающий за печать, если он является частью того же класса.

Принцип единственной обязанности используется в методологиях проектирования "от обязанности", таких как RDD и URDAD

O (open/closed principle, принцип открытости закрытости) принцип объектно-ориентированного программирования, устанавливающий следующее положение: "Программные сущности (классы, модули, функции и Т. П.) должны быть открыты для расширения, но закрыты для изменения"; это означает, что такие сущности могут позволять менять свое поведение без изменения их исходного кода. Это особенно значимо в производственной среде, когда изменения в исходном коде потребуют проведение пересмотра кода, модульного тестирования и других подобных процедур, чтобы получить право на использования его в программном продукте. Код, подчиняющийся данному принципу, не изменяется при расширении и поэтому не требует таких трудозатрат.

Термин "принцип открытости/закрытости" имеет два значения. Оба значения используют наследование для решения дилеммы, но цели, способы и результаты -- различны.

Бертран Мейер в основном известен как основоположник термина Принцип открытости/закрытости, который появился в 1988 году в его книге Object-Oriented Software Construction. Идея была в том, что однажды разработанная реализация класса в дальнейшем требует только исправления ошибок, а новые или измененные функции требуют создания нового класса. Этот новый класс может переиспользовать код исходного класса через механизм наследования. Производный подкласс может реализовывать или не реализовывать интерфейс исходного класса.

Определение Мейера поддерживает идею наследования реализации. Реализация может быть переиспользована через наследование, но спецификации интерфейса могут измениться. Существующая реализация должна быть закрыта для изменений, но новые реализации не обязаны использовать существующий интерфейс.

L (Liskov substitution principle, принцип подстановки Барбары Лисков) принцип в объектно-ориентированном программировании является специфичным определением Подтипа, предложенным Барбарой Лисков в 1987 году на конференции в основном докладе под названием Абстракция данных и иерархия.

Роберт С. Мартин определил этот принцип так:

Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.

Таким образом, идея Лисков о "подтипе" дает определение понятия Замещения -- если S является подтипом T, тогда объекты типа T в программе могут быть замещены объектами типа S без каких-либо изменений желательных свойств этой программы (например, корректность).

Этот принцип является Важнейшим критерием для оценки качества принимаемых решений при построении иерархий наследования. Сформулировать его можно в виде простого правила: тип S будет подтипом Т Тогда и только тогда, когда каждому объекту oS типа S соответствует некий объект oT типа T таким образом, что для всех программ P, реализованных в терминах T, поведение P не будет меняться, если oT заменить на oS.

Более простыми словами можно сказать, что поведение наследуемых классов не должно противоречить поведению, заданному базовым классом, то есть поведение наследуемых классов должно быть ожидаемым для кода, использующего переменную базового типа.

Саттер и Александреску в своем руководстве по использованию C++ для выражения этого принципа также используют фразу "подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс". По мнению данных авторов, публичное наследование в C++ можно употреблять только тогда, когда оно удовлетворяет принципу Лисков. Приватное наследование, по их же мнению, дозволено использовать для доступа к protected части базы и перекрытия виртуальных методов. В любом же ином случае, то есть для всего лишь повторного использования кода из базы, наследование применять нельзя.

Основания: использование публичного наследования для повторного использования кода приводит к тому, что внешний мир начинает считать класс Derived разновидностью класса Base, и возможно появление кода, явно использующего этот факт. Это сильно сужает простор для маневра архитектора в дальнейшем поддержании и рефакторинге класса Derived.

I (Interface segregation principle, принцип разделения интерфейса) один из пяти принципов проектирования классов в объектно-ориентированном программировании. Следование этому принципу помогает системе оставаться гибкой при внесении изменений в логику работы и пригодной для рефакторинга.

Роберт С. Мартин определил этот принцип так:

Клиенты не должны зависеть от методов, которые они не используют.

Принцип разделения интерфейсов говорит о том, что слишком "толстые" интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют.

D (dependency inversion principle, принцип инверсии зависимостей) важный принцип объектно-ориентированного программирования, используемый для уменьшения связанности в компьютерных программах. Входит в пятерку принципов SOLID.

Формулировка:

    - Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций. - Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Рефакторинг

В литературе достаточно подробно описаны методы рефакторинга, зачастую ими являются весьма нехитрые приемы, например:

    - если функция не используется - удалите функцию; - если название функции не отражает намерения программиста - переименуйте функцию.

Процессы написания нового кода и рефакторинга должны быть разделены - эту мысль замечательно выразил Кент Бек:

Рефакторинг не добавляет новые возможности, но добавление новых возможностей не должно изменять структуру кода.

Рефакторинг должен проводиться постоянно, даже во время изучения кода. Однако он выделяется в качестве отдельного этапа TDD (Test Driven Development, разработки через тестирование).

Даже у опытных программистов нередко бывает предчувствие, что в программе что-то не так, но не удается определить что именно. Фаулер замечательным образом систематизировал методы рефакторинга, а также отметил признаки плохого кода - так называемые "запахи", однако более фундаментальными для объектно-ориентированного подхода являются принципы SOLID.

Похожие статьи




Принципы SOLID - Программирование на языке C++

Предыдущая | Следующая