Перейти к содержимому

Конструирование системы. Часть 3 — Принципы программирования

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

DRY — Dont repeat yourself

Этот принцип гласит нам «не повторяйся» или в другой транскрипции «дублирование это зло». Этот принцип заключается в том, что нужно избегать повторений одного и того же кода. Лучше использовать универсальные свойства и функции.

KISS — Keep It Simple, Stupid

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

YAGNI — You Aren’t Gonna Need It

Этот принцип гласит нам «вам это не понадобится». Этот принцип заключается в том, что необходимо избегать написания избыточной функциональности, в которой нет необходимой надобности.

CQRS — Command-Query Responsibility Segretation

Этот принцип гласит нам «разделение ответственности на команды и запросы». Этот принцип заключается в том, что метод должен быть либо командой, выполняющей действие, либо запросом возвращающим данные, но не одновременно.

GOF — Шаблоны проектирования «банды четырёх»

Набор фундаментальных принципов организации кода (паттернов проектирования), для решения часто встречающихся проблем.

Порождающие Структурные Поведенческие
  • Абстрактная фабрика
  • Строитель
  • Фабричный метод
  • Прототип
  • Одиночка
  • Адаптер
  • Мост
  • Компоновщик
  • Декоратор
  • Фасад
  • Приспособленец
  • Заместитель
  • Цепочка обязанностей
  • Команда
  • Интерпретатор
  • Итератор
  • Посредник
  • Хранитель
  • Наблюдатель
  • Состояние
  • Стратегия
  • Шаблонный метод
  • Посетитель

SOLID — Single responsibility, Open-closed, Liskov substitution, Interface segregation и Dependency inversion

Тут у нас набор принципов относящихся к объектно ориентированному программированию. Рассмотрим каждый из них..

  • SRP: Signle responsibility (принцип единой ответственности)
    Принцип гласит, что каждый объект должен иметь только одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс.
    Это означает, что все его сервисы должны быть направлены исключительно на обеспечение этой обязанности.
  • OCP: Open-closed (принцип открытости-закрытости)
    Принцип гласит, что классы должны быть открыты для расширения, но закрыты для изменения.
    Это означает, что эти классы могут менять свое поведение без изменения их исходного кода.
  • LSP: Liskov subsititution (принцип подстановки Барбары Лисков)
    Принцип гласит, что функции которые используют базовый тип можно подменять на подтипы базового типа без ущерба для функционала приложения.
    Это означает, что функции смогут переключаться между подтипами выбранного типа.
  • ISP: Interface segregation (принцип разделения интерфейса)
    Принцип гласит, что интерфейсы должны быть специализированными.
    Это означает, что клиенты не должны зависеть от методов, которые они не используют.
  • DIP: Dependency inversion (принцип инверсии зависимостей)
    Принцип гласит, что модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций.
    Это означает, что необходимые классу сервисы необходимо внедрять через их интерфейсы, а не напрямую.

GRASP — General Responsibility Assignment Software Patterns

Тут у нас основные шаблоны распределения обязанностей в программном обеспечении..

  • Creator (Создатель экземпляров класса)
    Логично использовать паттерн если класс B содержит, агрегирует, активно использует и т.п. объекты класса A
  • Controller (Контроллер)
    Контроллер отвечает за обработку входных системных событий, делегируя обязанности по их обработке компетентным классам
  • Pure Fabrication (Чистая выдумка)
    Присвоить группу обязанностей с высокой степенью зацепления классу (синтезировать искусственную сущность, для высокого зацепления и низкой связности),
    который не представляет конкретного понятия из предметной области
  • Information Expert (Ответственность должна быть назначена тому, кто владеет максимумом необходимой информации для исполнения)
    Информационным экспертом может быть не один класс, а несколько
  • High Cohesion (Высокое зацепление)
    Обеспечить распределение обязанностей с высоким зацеплением
  • Inderection (Посредник)
    Присвоение обязанности по обеспечению связи между службами и компонентами промежуточному объекту
  • Low Coupling (Низкая связанность)
    Распределение обязанностей между объектами должно происходить так, чтобы степень связности оставалась низкой
  • Polymorphism (Полиморфизм)
    Обязанности распределяются для различных вариантов поведения с помощью полиморфных операций этого класса
    Каждая внешняя система имеет свой интерфейс
  • Protected Variations (Устойчивый к изменениям)
    Идентификация точек возможных изменений или неустойчивость системы, и распределить обязанности таким образом,
    чтобы обеспечить устойчивую работу системы.

PACKAGES — Принципы организации наборов классов

Тут у нас набор принципов относящихся к наборам классов (пакетов). Рассмотрим их…

  • REP: Reuse-release Equivalence Principle (принцип эквивалентности повторного использования и выпусков)
    это означает что содержимое пакета должно принадлежать связной группе, а не просто смесь файлов
  • CCP: Common Closure Principle (принцип согласованного изменения)
    это означает что содержимым пакета должно являться то, что нужно менять по одной общей причине
  • CRP: Common Reuse Principle (принцип совместного повторного использования)
    это означает что содержимым пакета должно включаться то, что используется совместно
  • ADP: Acyclic Dependencies Principle (принцип ацикличности зависимостей)
    это означает что циклы в графе зависимостей компонентов недопустимы
  • SDP: Stable Dependencies Principle (принцип устойчивых зависимостей)
    это означает что изменчивые пакеты не стоит делать зависимыми от пакетов которые трудно изменять
  • SAP: Stable Abstractions Principle (принцип устойчивости абстракций)
    это означает что стабильный компонент должен быть абстрактным (состоять из интерфейсов и абстрактных классов) — чтобы его устойчивость не препятствовала расширению, а нестабильный компонент должен быть конкретным (состоять из реализаций) — чтобы его неустойчивость позволяла ему легко изменять код внутри него.

Дополнительно — Объектная гимнастика

В качестве бонуса, предлагаю еще воспользоваться набором упражнений, в разы повышающих читабельность, тестируемость, понятность и поддерживаемость кода (если они вам не понравятся, это нормально, просто еще не время чтобы осознать их необходимость  🙂  ) :

  1. Только один уровень отступа в методе
    Большое количество вложенности языковых конструкций в коде сильно запутывает логику, в следствии чего код становится «плохим».
  2. Не используйте Else
    Конструкции использующие else сложны, с учётом того что они часто в последствии становятся вложенными.
  3. Оберните все примитивные типы
    Необходимо оборачивать в классы все примитивные типы, обладающие поведением. Например — деньги, время, .. т.к. это избавит от дублирования и упразднит работу с ними.
  4. Коллекции первого класса
    Классы содержащие коллекцию, должны содержать только её, и методы управляющие этой коллекцией.
  5. Одна точка на строку (в PHP это два -> на строку, fluent interface не считаются)
    map->person->weapon->reload() — Прямо противоречит «закону Деметры» — который гласит, что можно обращаться только к друзьям класса.
  6. Не используйте сокращения
    обычно места в коде, где вы используете сокращения, требуют рефакторинга,…
  7. Сохраняйте сущности короткими
    15 классов в пакете/области,  200 строк в файле включая комментарии, 10 методов в классе — из среднего сколько человек сможет на лету удержать в голове
  8. Никаких классов с более чем 2 атрибутами
    если рассматривать один из атрибутов как структуру данных (struct, assoc array) — всё сразу встаёт на свои места.
  9. Никаких геттеров, сеттеров, и свойств
    Если внедрение геттера еще хоть как-то можно оправдать, то внедрение сеттера, это скорее всего перекладывание обязанностей на потребителей класса.

назад

Опубликовано вОбщее