Стив Макконнелл - Совершенный код

Совершенный код С.Макконнелл

Часть 1 ОСНОВЫ РАЗРАБОТКИ ПО

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

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

Что такое конструирование ПО (стр. 5)

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

Что не входит в конструирование ПО

  • разработка архитектуры приложения
  • проектирование UI
  • тестирование системы и ее сопровождения

Что делают до начала конструирования ПО(кодирование):

  1. Выработка требований
  2. Проектирование архитектуры

Метафоры (стр. 20) Сравнение конструирования По с возведением здания указывает на необходимость начальной подготовки к проекту и проясняет различия между крупными и небольшими проектами.

Глава 3 Предварительные условия

Предварительные условия (стр. 25) Вы забрались на гору определения проблемы, прошли милю по пути выработки требований, сбросили грязную одежду и фонтана архитектуры и искупались в чистых водах подготовленности. Теперь просветите профанов в особенности процесса разработки ПО до начала кодирования. Исправление ошибки к началу конструирования обходится в 10-100 раз дешевле, чем ее устранение в конце работы над проектом.

Определение проблемы - это просто формулировка сути проблемы без намеков на возможные решения.Не имея хорошего определения проблемы можно потратить усилия на решение не той проблемы.(стр. 30)

Выработка требований или по другому спецификация, "анализ" Явные требования позволяют не годать что хочет User Адекватное определение требований от пользователя должно быть проработанно так что бы свести к минимуму их последующие изменения на следующих этапах конструирования и т.д.(стр. 36)

Типичные компоненты архитектуры на которые следует обратить внимание:(стр. 42-46)

  • организация программы Архитектура должна включать общее описание системы для согласованной картины и ответственности каждого компонета и обоснованность того или иного решения.
  • основные классы Архитектура должна определять основные классы и их взаимодействие и область ответственности с иерархией
  • организация данных
  • бизнес-правила
  • пользовательский интерфейс
  • управление ресурсами
  • безопасность
  • производительность
  • масштабируемость
  • взаимодействие с другими системами
  • интернационализация/локализация
  • ввод/вывод
  • обработка ошибок
  • отказоустойчивость
  • возможность реализации архитектуры
  • избыточная функциональность
  • купить или создавать самим
  • повторное использование
  • стратегия изменений
  • общее качество архитектуры

Глава 4 Основные решения при конструировании

Если не проведено адекватное проектирование архитектуры во время конструирования вы можете решать верную проблему неверным способом

Часть 2 ВЫСОКОКАЧЕСТВЕННЫЙ КОД

Глава 5 Проектирование при конструировании

Уровни проектирования (стр. 79)

  1. Программная система (вся система)
  2. Разделение системы на подсистемы/пакеты
  3. Разделение подсистемы/пакетов на классы
  4. Разделение классов на данные и методы
  5. Проектирование методов

Уровень 2 Подсистемы - это модуль работы с БД, модуль GUI, создание отчетов, бизнес-правила модуль.Суть разделение подсистемы в знании составных блок и как они будут взаимодействовать и ограничивать доступ. Если все подсистемы будут взаимодействовать то выгода от их разделения исчезает.

Вопросы

  • В скольких разных частях системы нужно разобраться чтобы внести изменение в одну из частей системы?
  • Что будет если задействовать модуль бизнес-правил в другой системы?
  • Что будет если добавить новый пользовательский интерфейс (инструмент тестирования командной строки)
  • Что произойдет если вы захотите перенести модуль хранения данных на удаленный компьютер?

Простота отношений между система

  1. Самые простое отношение, система вызывает методы другой системы.
  2. Среднее отношение, одна подсистема содержит классы другой.
  3. Сложные отношения, наследование классов одной подсистемы от классов другой.

Программа должна быть ациклическим графом.(стр. 81) Т.е. программа не должна содержать цикличиских отношений, класс А использует класс B, класс B использует класс C,а класс C использует класс А

Часто используемые подсистемы:(стр. 82)

  • Бизнес-правила - это реализуемые законы,процедуры бизнеса в компьютерной системе.Для упрощения бизнес-логики используйте ее выражение в форме таблиц(глава 18)
  • Пользовательский интерфейс , его изоляция позволяет при его изменении не изменять остальную программу.Состоит из подсистем или классов, отвечающих за GUI, интерфейс командной строки, работу меню, работу с окнами.
  • Доступ к БД,вы должны скрыть детали реализации доступа к БД, что бы программа работала с данными в терминах бизнес-проблемы, т.е. должны быть отдельные обьекты посредники данных. Что бы можно было изменить структуру БД и самому БД без внесения изменений в остальную часть программы.

Уровень 3 Определить все классы программы Например, подсистема доступа к БД может быть разделена на классы доступа к данным и классы хранения данных и класс метаданных БД. Важно определить детали взаимодействия каждого класса с остальными элементами системы, особенно интерфейса.Суть в декомпозиции подсистемы до уровня детальности классов.

Уровень 4 Разделение каждого класса на методы. Полное определение методов класса позволяет лучше понять его интерфейс, что может подтолкнуть к изменению интерфейса т.е. вернуться на Уровень 3

Уровень 5 Определение детальной функциональности отдельных методов. Написание псевдокода, поиск алгоритмов, оптимальный организацией фрагментов метода и написание кода.

Компоненты проектирования:(стр. 84)
  1. Определите обьекты реального мира.
    • обьекты и их аттрибуты (методы и данные)
    • действие над каждым обьектом
    • действие каждого обьекта над другим обьектами (наследование и композиция)
    • открытие и закрытые части каждого обьекта
    • интерфейс каждого обьекта т.е. его ткрытые методы для работы с ним.
  2. Определите согласованные абстракции. Абстракция позволяет задействовать концепцию и игнорировать детали.Имея дело с составным обьектом вы имеете дело с абстракцией. Пример, дом это абстракция над деревом,гвоздями и стеклом, а город это абстракция над множеством домов. Абстракция - один из главных способов борьбы со множестью реального мира. Разработчики создающие системы на уровне волокон древесины то эти системы сложно понять и поддерживать и они сложны,а создание абстракции на уровне интерфейсов методов, классов и пакетов способствует безопасному и быстрому программированию.
  3. Инкапсулируйте детали реализации Если абстракция говорит ""рассмотреть обьект с общей точки зрения"" то инкапсуляция говорит ""это обьект с другой точки зрения рассмотреть нельзя"". Инкапсуляция позволяет управлять сложностью, блокируя доступ к ней.
  4. Используйте наследование если оно упрощает проектирование. Если одни обьекты аналогичны другим, за исключением нескольких отличий создайте общий случай и наследуйтесь от него как частный случай. Наследование - определение сходств и различий, уменьшение дублирования, упрощает разработку используя универсальные методы для выполнения всего, что основанно на общий свойствах.
  5. Скрывайте секреты Секретом может быть и источник вероятных изменений (т.е. если он будет открыт и будет использован другими источниками то их придется тоже переделывать), формат файла, реализация типа данных, возможные ошибки использования. Небольшие изменения не должны распространяться за его интерфейс т.е. только закрытая часть может изменяться. Секретом может быть скрывающаяся сложность, позволяя разработчикам забыть о ней при работе.

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

Сопряжение

Сопряжение должно быть простым и слабым и слабозависящим Критерии оценки сопряжения

  • Обьем: чем больше связей между модулями или классами тем выше сопряжение чем больше передаваемых параметров тем выше
  • Видимость: чем лучше видно, явная связь между модулями или классами тем меньше сопряжение и если модули или классы работают через общее состояние тем выше сопряжение. Связь через глобальные данные должна быть задокумментированна.
  • Гибкость: высокая сопряженность если модули обмениваются обьектами а не простыми данными слабая сопряженность будет если обьекты будут меньше знать о работе друг друга."
Виды сопряжения (стр. 98)
  1. Простое сопряжение посредством данных-параметров элементарные данные
  2. Простое сопряжение посредством обьекта если модуль создает обьект
  3. Жестче сопряжение посредством обьекта-параметра это хуже сопряжение так как принимающий модуль или класс должен обладать информацией об принимающем обьекте что бы работать с ним.
  4. Самый жесткий, семантическое сопряжение когда модуль полагается на семантическое знание внутреннего устройства работы другого модуля. Когда модуль принимает параметр от другого модуля в виде управляющего флага вместо паречисления или обьекта т.е. передающий модуль должен знать что принимающий модуль будет делать с флагом. Использование глобальных данных для обмена информацией. Предположение о внутренней механике, последовательности работы модуля.Передача неполного обьекта ,частично заполненного Передача общего обьекта но работать с производным.

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

Популярные шаблоны: Адаптер, Мост,Декоратор,Фасад,Фабричный метод,Наблюдать,Одиночка,СТратегия,Одиночный метод. Применение шаблонов - это эффективный способ управления сложностью

Методики проектирования (стр. 107)

  • Низходящее top-down сначала определение общих классов далее повышение детальности к производным классам
  • Восходящее bottom-up со специфики и переход ко всебольшей общности
  • Экспереминтальное прототипирования помогает ответить на неиспытанные вопросы методы написания кода проверяющий эти неясности.

Глава 6 Классы

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

Если класс реализует более одного абстрактного класса или вы не смогли однозначно ответить какой именно является реализацией абстракции этот класс то самое время реорганизовать класс или разнести по отдельными реализациям. Так как такой класс не может дать согласованный интерфейс, внем будет намешаны другие методы.(стр. 132)

Хорошая инкапсуляция (стр. 138) Сокрытие деталей реализации и следовательно неправильного использования убирает сложные ошибки семантической инкапсуляции (т.е. неправильное использование) сквозь интерфейс

Наследование (стр. 141) Барбара Лисков заявила, что наследование стоит использовать только если производный класс действительно ""является"" более специализированной версией базового иначе если производный класс не будет полностью придерживаться контракта интерфейса базового класса то не следует использовать наследования, а сделайте включение или изменить абстракцию наследования. Если вам нужна реализация класса но не его интерфейс не наследуйте его, использйуте включение. Цель наследования уменьшить сложность и использовать более простой код без дублирования.

Остерегайтесь пустых переопределенных методов лучше избавиться от причины проблемы чем от ее следствий. Это нарушение абстракции изменяя семантика интерфейса.Как следствие развитие такого подхода поведение производных класов будет сильно отличаться от базовых интерфейсов. Решение: включить обьект проблемы в класс. (стр. 143)

Управление сложностью (стр. 146)

  • Если несколько классов имеют общие данные но не формы поведения , создайте общий обьект и включите его во все эти классы.
  • Если несколько классов имеют общие формы поведения но не данные, сделайте эти классы производными от общего класса.
  • Если несколько классов имеют и общие данные и формы поведения сделайте их производными от базового класса определяющего общие данные и методы. Наследование используйте если хотите что бы интерфейс определялся базовым классовых. Включение используйте если хотите сами контролировать интерфейс

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

Класс не должен содержать только данные он должен иметь поведение тоже. (стр. 152)

Ключевые моменты (стр. 156)

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

Глава 7 Высококачественные методы

Метод - это отдельная функция или процедура, выполняющая одну задачу (стр. 157) Имя метода, документация метода, форматирование входных переменных явно изменяются видно с названия, неизменяет глобальные переменные, четкая цель метода,защищенный от плохих данных, нет магических чисел, мало принимает параметров

Причины создания методов (стр. 160)

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

Проектирование на уровне метод (стр. 163)

  • Функциональная связанность - когда метод выполняет только одну операцию Пример: метод GetCustomerName(), EraseFile() если их имя соответсвует их имени
  • Последовательная связность - операции выполняются строго в определенном порядке и использует данные предыдущих этапов и не формирует в целом единую функцию
  • Коммуникационная связность - когда операции в методе используют одни и те же данные и не связаны между собой. Реализовать разнисением этих операций в отдельные методы и вызвать в одном общем.
  • Временная связность - когда код обьединяется актуальностью времени выполнения.Пример: при загрузке системы Startup() не выполняйте конкретный код,а вызывайте методы из него тогда суть метода будет согласование действий, а не их выполнение.

Неприемлемые методы связности (стр. 165)

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

Удачные имена методов (стр. 167) Главной задачей имени метода это ясное понятное описание сути метода.

  • Описывайте все что метод выполняет, что бы такие имена не получались длинными и несуразными не делайте больших методов и тогда не будут методы с побочными эффектами
  • Избегать неоднозначных имен методов для этого у метода должна быть четкая цель.
  • Для именования функции используйте описание возможного возвращения значения. Имя процедуры должно состоять ""выразительный глагол+обьект"" , а функция содержит имя возвращаемого обьекта

Параметры методов (стр. 170) Порядок "входные - изменяемые - выходные"

Когда использовать функции (стр. 177) Функция - это метод, возвращающий значения Процедура - это метод, не возвращающая значение

Глава 8 Защитное программирование

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

  • Утверждение asset используется для проверки данных из проверенного источника.
  • Обработчик ошибок для данных поступивших из внешнего источника. Утверждения применяются для ошибок, которые никогда не должны происходить.

Способы обработки ошибок: (стр. 189)

  • вернуть нейтральное значение
  • заменить следующим корректным блоком данных
  • подставить ближайшее доступное значение
  • записать предупреждение в файл
  • вызвать обработчик ошибки
  • прекратить выполнение Корректность требует верных результатов Устойчивость требует продолжение работы, даже с частично неверными результатами

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

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

Альтернатива исключениям. (стр. 198) Вам всегда следует принимать во внимание все возможные методы обработки ошибок:локальную обработку,возврат кода ошибки,запись отладочной информации в файл, прекращение работы программы и др. Обрабатывать ошибки с помощью исключений только потому-что это позволяет язык - классический пример программирования на языке, а не с использованием языка.

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

Путь данных: (стр. 200)

  1. Источник небезопасных данных
    • графический интерфейс
    • интерфейс командной строки
    • данные в режиме реального времени
    • внешние файлы
    • др. внешние обьекты
  2. Проверенный класс отвечающий за очистку данных, баррикада.
  3. Внутренний класс, который может считать данные корректными.

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

Глава 9 Процесс программирования псевдокодом

Псевдокод (стр. 212) Должен описывать намерение/назначение/поведение кода,а не метод/способ решения или то как его писать Методика создания классов и методов:

  • псевдокод
  • через тестирование т.е. сначало тестируем потом пишем код
  • рефакторинг, усовершенствование ""пахнущего"" кода
  • проектирование по контракту, пре- и пост- условиям

Пример псевдокода:

    Отследить текущее число 
    Если другой ресурс доступен
        Выделить структуру для диалогового окна
         Если структура для диалогового окна может быть выделена
         ...
        Конец если
    Конец ксли
    Вернуть true  если новый ресурс был создан, иначе вернуть false

Часть 3 ПЕРЕМЕННЫЕ

Глава 10 Общие принципы использования переменных

  • Избегайте переменных имеющих скрытый смысл т.е. в одном случае ее значение означает не то что бы значило в другом случае условия
  • Каждая переменная для одной цели и расположенная рядом с использованием

Глава 11 Сила имен переменных

Сила имен переменных (стр. 254-261) Имя переменной должно полно и точно описывать сущность Можно сформулировать суть переменной в словах Пример:число членов олимпийской зборной команды США ""numberOfPepleOnTheUsOlympicTeam"" это имя не нужно расшифровывать его можно просто прочитать Имя переменной ""date"" ничего не говорит о датеиначе выглядит ""currentDate"" это уже лучше, мы уже значем что это текущая дата.

Хорошее имя выражает ""что"" , а не ""как"" Оптимальная длина имени до 40 символв, встреднем от 9-15. ""NumCustomer"" это общее число заказчиков ""CustomerNum"" это номер текущего заказчика

  • Именование переменных цикла i,j,k в случае вложенных циклов имеет смысл давать индексам осмысленные имена ""firstItem"" ""temIndex""

  • Именование переменных статуса Не используйте имя ""flag"" Нужно присваивать выразительные имена сравнимые со значениями перечислений,именованных констант Пример

не выразительных и загадочных имен:                                 выразительно:
if(flag)                                                            if(dataReady)
if(statusFlag & 0x0F)                                               if(characterType & PRINTABLE_CHAR) перечисление
if(printFlag == 16)                                                 if(reportType == ReportType_Annual) именованная константа

flag=0x1                                                            dataReady = true
statusFlag=0x80                                                     characterType=CONTROL_CHARACTER
printFlag=16
код не должен содержать загадок, его просто можно прочитать.
  • Именование временных переменных Подумайте о ее роли, имя temp плохо об этом говорит. Пример: плохо temp=sqrt(вычисление) хорошо discriminant=sqrt(вычисление) ясное использование root=(-b + discriminant)/(2*a)

  • Именование булевых переменных Полезные имена ""done"" признак завершения цикла, ""error"", ""found"" , ""succes"" , ""ok"" Присваивайте имена которые подразумевают тип данных bolean Пример: имя ""status"" не означает что оно может содержать bolean ,а вот ""statusOK"" - может, еще имя ""sourceFile"" можно заменить на ""sourceFileAvailable"",""sourceFileFound"",""processingComplete"" Добавление префикса ""is"" может затруднить чтение ""if(isFound)"" хуже читается чем ""if(found)"" , а ""isStatus"" вообще не имеет смысла.

  • Именование значений для перечисления Color может быть таким: Color_Red. Color_Green Существуют мнения о именовании значений перечислений как обычную переменную или константу

  • Именование констант CYCLE_NEEDED должно характеризовать абстрактную сущность, а не конкретное значение Пример: FIVE=5.5 это плохое имя для константы

Сила конвенции именования (стр. 263)

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

  • Имя типа отличается от имени переменных Пример: тип ""Widget"", а переменная ""widget""
  • Индифицируйте имена глобальных переменных через ""g_"" ""g_RunningTotal""

Пример для Java:

  • имя класса ""ClassName""
  • имя метода ""RoutineName""
  • имя переменных-членов класса доступных только в классе ""m_"" ""m_ClassVariables""
  • имя типа ""TypeName""
  • имя перечисления ""EnumeratedType"" т.е. форма множественного числа
  • имя значений для перечислений ""Base_EnumeratedType"" т.е. с имененм самого перечисления
  • имя локальной переменной ""localVariables"" т.е. со строчной буквы
  • имя парметров методов как и локальных переменных
  • имя констант ""MACRO""

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

Глава 12 Целочисленные числа

Проверяйте целочисленность операции деления Пример: 7/10 при целочисленном делении не равно 0,7 а равно либо 0 либо ближайшему целому либо что-то еще. Проверяйте на переполнение целых чисел Пример: 250*300=9464 вместо 75000 та как (75000-65536=9464) также проверяйте переполнение промежуточных результатов в вычислении.

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

  • измените тип переменной на тип с большей точностью
  • используйте двоично-десятичные переменные

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

Часть 4 ОПЕРАТОРЫ

Глава 14 Организация последовательного кода

Организация последовательно кода - упорядочение зависимостей Если зависимости между выражениями требуют размещения их в определенном порядке, сделайте эти зависимости явными. Пример: неявная зависимость: revenue.ComputeMonthly(); revenue.ComputeQuartle(); явная зависимость: dataRead=ReadData(); result=CalculateResultFromData(dataRead);

  • Не пишите код требующий порядковой последовательности(или изолируйте эту последовательность за интерфейсом)
  • Если пишите последовательный код то сделайте зависимости явными
  • Если не получается сделатиь явную зависимость тогда задокументируйте этот момент

Пишите код который читается сверху вниз, разбивая взаимосвязанные блоки. (стр. 343)

Глава 15 Условные операторы

Размещайте нормальный вариант работы программы в блоке ""If"" а не ""else"" , что позволит сфокусироваться в одном направлении логики при чтении кода. Избегайте глубокой вложенности блока ""if"" не более 3-х.Используйте разбивку на блоки, сохраняйте промежуточный результат во временную переменную, декомпозируйте на методы, используйте полиморфизм.

Глава 18 Табличные методы

Табличный метод - это схема , позволяющая искать информацию в таблице,а не использовать для этого логические выражения как ""if,case"". Таблица - это массив или коллекция Типы таблиц:

  • индексная с прямым доступом по ключу (map)
  • промежуточный индекс
  • ступенчатый доступ

Общие принципы уменьшения сложности. Чем больше вариантов выбора тем больше сложность кода, разбивая этот код на куски мы уменьшаем сложность с которой придется столкнуться в каждый момент времени. Подсчет точек сложности не каждый из эквивалентных операторов +1: if,while,for,and,or,case Если метод обладает более чем 10 точками рекомендуется разделение.

Часть 5 УСОВЕРШЕНСВОВАНИЕ КОДА

Глава 20 Методики повышения качества ПО

  • Определиться с целью улучшения качества (Корректность, практичность, эффективность,надежность,целостность,адаптируемость,правильность,живучесть), эти цели не могут быть достигнуты одновременно, поэтому выбирая одни цели мы будем неизбежно жертвовать другими характеристиками качества но это уже будет осознанный выбор!
  • Явный контроль качества т.е. стараться писать качественый код.
  • Придерживаться принципов разработки ПО (выработка требований, проектирование...)
  • Стратегия тестирования учитывающие требования к системе, ее архитектуру и проетирование. Плюс к тестированию добавить еще добавить способы позволяющие обнаружить ошибку с другой стороны, есть разные методы обнаружения.463стр
  • Делайте обзоры системы самостоятельно читая и приглашайте внешний аудит.

Чем раньше обнаружение и исправление тем дешевле и быстрее будет продукт. (стр. 459)

Глава 22 Тестирование выполняемое разработчиками

Тестирование не доказывает отсутствие ошибок . Если нет ошибок возможно сами тесты неполны. Тестирование не повышает качества ПО - оно указывает на качество, но не влияет на него. Тип теста: Чистый тест нацелен на проверку работы кода Грязный тест нацелен на попытку нарушить код и грязные тесты должны преобладать в тестировании.(стр. 492)

Классификация ошибок по распространенности(стр. 509) 25% структурные ошибки 22% ошибки данных 17% ошибки релизации данных 10% ошибки в конструировании 9% ошибки в интеграции 8% ошибки в функциональных требованиях 3% ошибки в определении или выполнении тестов 2% ошибки в системе или в архитектуре ПО Но разные исследование показывают разные результаты

Ошибки конструирования 45-75% из всех ошибок т.е. написание кода, остальные ошибки в проетировании и выработке требований (стр. 511)

Используйте инструменты мониторинга покрытия кода тестами Используйте автоматическое тестирование оно лучше ручного (стр. 516)

Глава 24 Рефакторинг

Факторинг - это декомпозиция на составные части. Рефакторинг - это изменение работоспособного кода, не влияющее на поведение программы, если рефакторить не рабочий код то это называется хакерство. Причины рефакторинга:

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

Глава 25 Стратегии оптимизации кода

Оптимизация кода Улучшение производительности - это ""антирефакторинг"" изменения ухудшают внутреннюю структуру программы ради повышения ее производительности.

Принцип Парето

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

Не стоит ставить оптимизацию важнее чем архитектура

Частые причины снижения эффективности

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

Глава 26 Методики оптимизации кода

Замена сложных логических выражений на обращение к таблице (табличный метод), экономит время выполнения от 30% (стр. 600)

Размыкание цикла

  • Замыкание цикла - это принятие решения внутри цикла,а размыкание это принятие решения снаружи цикла
    if(true){
      for()...
    else{
      for()...
    

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

  • Развертывание цикла Способ написания цикла при каждой итерации выполняя два и более случая
    i=0
    while(i<count-2){
         a[i]=i;
         a[i+1]=i+1;
         a[i+2]=i+2;
         i=i+3;
    }
    
  • Уменьшения операций в циклах
  • Кеширование часто необходимой и тяжелой информации
  • Замена возведения в степень на умножение, а умножение на сложение. Или замена умножения и деления на операции сдвига
  • Предварительные вычисления выражений"

Часть 6 СИСТЕМНЫЕ ВОПРОСЫ

Глава 28

Индивидуальные различия среди проффесионалов: соотношение времени кодирования 20:1 соотношение размера программ 5:1 соотношение времени отладки 25:1 соотношение скорости выполнения 10:1 Тесты не нашли взаимосвязь между опытом и качеством кода или производительность программистов проффесионалов.

Часть 7 МАСТЕРСТВО ПРОГРАММИРОВАНИЯ

Глава 31 Форматирование и стиль

Основная теорема форматирования гласит, что хорошее визуальное форматирование показывает логическую структуру программы, а не ее красоту.

Глава 32 Комментарии к коду

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

Глава 33 Личность

Эффективное программирование - это поиск способов компенсировать ваши интелектуальные способности к пониманию кода. Так как человек ограничен нужно писать код так что бы было легко его понять другому человеку.

Следует развивать любопытство к техническим вопросам так как ваши знания постоянно устаревают.

Как найти время для поиска эффективных способов работы:

  • изучите процесс разработки По из книг или на собственном опыте Не тратьте время на компанию не учитывающую ваших интересов в развитии, устареете за 3 года.
  • экспериментируйте с процессами программирования и разработки Быстро совершая ошибки и извлекая из них уроки вы облегчаете себе путь к эффективному программированию.
  • читайте о решении проблем помимо самой практики. Книга о решении проблем Джеймса Адамса ""Conceptual Blockbusting 2001""
  • знания расслабившегося программиста быстро устаревают (стр. 803)

Программируйте в терминах проблемной области, решения программы на уровне абстракции. Это один из способов борьбы со сложностью, разделяя программы на уровни абстракции изолируя нижние уровни от верхних, в верхних уровнях есть только термины проблемной области, а не детали реализации и средства языка и возможности ОС, это все нижний уровень. В верхнем уровне реализуются за счет высоуровневой абстракции позволяющей работать с проблемой в ее терминах,а не языка. (стр. 825)

Если вы не можете протестировать класс из-за его сильной сопряженности то перепишите класс. Если вы не можете использовать повторно код то это то же сильное сопряжение которое следует понизить. (стр. 829)