
- •Оглавление
- •Об авторе
- •Благодарности
- •Предисловие
- •Глава 1. Держим оборону
- •На пути к хорошему коду
- •Готовьтесь к худшему
- •Что такое защитное программирование?
- •Этот страшный, ужасный мир
- •Технологии защитного программирования
- •Выберите хороший стиль кодирования и пользуйтесь крепкой архитектурой
- •Пишите код без спешки
- •Не верьте никому
- •Стремитесь к ясности, а не к краткости
- •Не позволяйте никому лезть туда, где ему нечего делать
- •Включайте вывод всех предупреждений при компиляции
- •Пользуйтесь средствами статического анализа
- •Применяйте безопасные структуры данных
- •Проверяйте все возвращаемые значения
- •Аккуратно обращайтесь с памятью (и другими ценными ресурсами)
- •Инициализируйте все переменные там, где вы их объявили
- •Объявляйте переменные как можно позже
- •Пользуйтесь стандартными средствами языка
- •Пользуйтесь хорошими средствами регистрации диагностических сообщений
- •Выполняйте приведение типов с осторожностью
- •Подробности
- •Ограничения
- •Какие ограничения налагать
- •Снятие ограничений
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Да в чем проблема?
- •Знайте своих клиентов
- •Что такое хорошее представление?
- •Размещение скобок
- •Скобки в стиле K&R
- •Расширенный стиль скобок
- •Стиль Уайтсмита (с отступами)
- •Другие стили скобок
- •Единственно верный стиль
- •Внутрифирменные стили (и когда их придерживаться)
- •Установка стандарта
- •Религиозные войны?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Зачем нужны хорошие имена?
- •Каким объектам мы даем имена?
- •Игра в названия
- •Описательность
- •Техническая корректность
- •Идиоматичность
- •Тактичность
- •Технические подробности
- •Имена переменных
- •Имена функций
- •Имена типов
- •Пространства имен
- •Имена макросов
- •Имена файлов
- •Роза пахнет розой
- •Соблюдайте единообразие
- •Связывайте имя с содержимым
- •Извлекайте выгоду из выбора имени
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Самодокументируемый код
- •Техника написания самодокументируемого кода
- •Пишите простой код с хорошим форматированием
- •Выбирайте осмысленные имена
- •Разбивайте код на самостоятельные функции
- •Выбирайте содержательные имена типов
- •Применяйте именованные константы
- •Выделяйте важные фрагменты кода
- •Объединяйте взаимосвязанные данные
- •Снабжайте файлы заголовками
- •Правильно обрабатывайте ошибки
- •Пишите осмысленные комментарии
- •Практические методологии самодокументирования
- •Грамотное программирование
- •Инструментарий документирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Что есть комментарий в коде?
- •Как выглядят комментарии?
- •Сколько комментариев требуется?
- •Что помещать в комментарии?
- •Не нужно описывать код
- •Не подменяйте код
- •Как сделать комментарии полезными
- •Не отвлекаться
- •На практике
- •Замечание об эстетичности
- •Единообразие
- •Четкие блочные комментарии
- •Отступы в комментариях
- •Комментарии в конце строки
- •Помощь в чтении кода
- •Стиль должен обеспечивать легкость сопровождения
- •Границы
- •Флажки
- •Комментарии в заголовке файла
- •Работа с комментариями
- •Помощь при написании программ
- •Заметки об исправлении ошибок
- •Устаревание комментариев
- •Сопровождение и бессодержательные комментарии
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Откуда что берется
- •Механизмы сообщения об ошибках
- •Без обработки ошибок
- •Возвращаемые значения
- •Переменные, содержащие состояние ошибки
- •Исключения
- •Сигналы
- •Обнаружение ошибок
- •Обработка ошибок
- •Когда обрабатывать ошибки
- •Варианты реагирования
- •Последствия для кода
- •Подымаем скандал
- •Управление ошибками
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Что такое инструмент программирования?
- •А зачем они нужны – инструменты?
- •Электроинструменты
- •Выясните, каковы его возможности
- •Научитесь им управлять
- •Выясните, для каких задач он пригоден
- •Убедитесь, что он работает
- •Имейте четкие данные о том, как получить дополнительные сведения
- •Узнайте, как получить новые версии
- •Какой инструмент необходим?
- •Средства редактирования исходного кода
- •Средства построения кода
- •Инструменты для отладки и тестирования
- •Средства поддержки языка
- •Инструменты различного назначения
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Проверка на подлинность
- •Кто, что, когда, зачем?
- •Зачем тестировать
- •Кому тестировать
- •В чем состоит тестирование
- •Когда тестировать
- •Типы тестирования
- •Выбор контрольных примеров для блочного тестирования
- •Архитектура и тестирование
- •Руками не трогать!
- •Анатомия провала
- •Справлюсь ли я сам?
- •Система контроля ошибок
- •Обсуждение ошибок
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Реальные факты
- •Природа этого зверя
- •Взгляд с высоты птичьего полета
- •Взгляд с поверхности земли
- •Взгляд из глубины
- •Борьба с вредителями
- •Обходная дорога
- •Правильный путь
- •Охота за ошибками
- •Ошибки этапа компиляции
- •Ошибки этапа исполнения
- •Как исправлять ошибки
- •Профилактика
- •Отладчик
- •Средство проверки доступа к памяти
- •Трассировщик системных вызовов
- •Дамп памяти
- •Журналирование
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Языковые барьеры
- •Интерпретируемые языки
- •Компилируемые языки
- •Делаем слона из мухи
- •Выполнение сборки
- •Что должна уметь хорошая система сборки?
- •Простота
- •Единообразие
- •Повторяемость и надежность
- •Атомарность
- •Борьба с ошибками
- •Механика сборки
- •Выбор целей
- •Уборка
- •Зависимости
- •Автоматическая сборка
- •Конфигурация сборки
- •Рекурсивное применение make
- •Мастер на все руки
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Что такое оптимизация?
- •От чего страдает оптимальность кода?
- •Доводы против оптимизации
- •Альтернативы
- •Нужна ли оптимизация
- •Технические подробности
- •Убедитесь, что нужна оптимизация
- •Определите самую медленную часть кода
- •Тестирование кода
- •Оптимизация кода
- •После оптимизации
- •Методы оптимизации
- •Конструктивные изменения
- •Модификация кода
- •Как писать эффективный код
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Риски
- •Наши оппоненты
- •Оправдания, оправдания
- •Ощущение незащищенности
- •Опасный проект и архитектура
- •Переполнение буфера
- •Встроенные строки запросов
- •Условия гонки
- •Целочисленное переполнение
- •Дела защитные
- •Технология установки системы
- •Технология конструирования программного обеспечения
- •Технологии реализации кода
- •Технологии процедуры
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Программирование как конструкторская работа
- •Что нужно проектировать?
- •Хороший проект программного продукта
- •Простота
- •Элегантность
- •Модульность
- •Хорошие интерфейсы
- •Расширяемость
- •Избегайте дублирования
- •Переносимость
- •Идиоматичность
- •Документированность
- •Как проектировать код
- •Методы и процедуры проектирования
- •Инструменты проектирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Что такое программная архитектура?
- •План программы
- •Точки зрения
- •Где и когда этим заниматься?
- •Для чего она применяется?
- •Компоненты и соединения
- •Какими качествами должна обладать архитектура?
- •Архитектурные стили
- •Без архитектуры
- •Многоуровневая архитектура
- •Архитектура с каналами и фильтрами
- •Архитектура клиент/сервер
- •Компонентная архитектура
- •Каркасы
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Гниение программного обеспечения
- •Тревожные симптомы
- •Как развивается код?
- •Вера в невозможное
- •Как с этим бороться?
- •Как писать новый код
- •Сопровождение существующего кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Мартышкин труд
- •Нетерпеливый
- •Кодер (Code Monkey)
- •Гуру
- •Псевдогуру
- •Высокомерный гений
- •Ковбой
- •Плановик
- •Ветеран
- •Фанатик
- •Монокультурный программист
- •Лодырь
- •Руководитель поневоле
- •Идеальный программист
- •И что из этого следует?
- •Для глупцов
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 17. Вместе мы – сила
- •Команды – общий взгляд
- •Организация команды
- •Методы управления
- •Разделение ответственности
- •Организация и структура кода
- •Инструменты для групповой работы
- •Болезни, которым подвержены команды
- •Вавилонская башня
- •Диктатура
- •Демократия
- •Большой Каньон
- •Зыбучие пески
- •Лемминги
- •Личное мастерство и качества, необходимые для работы в команде
- •Общение
- •Скромность
- •Разрешение конфликтов
- •Обучение и приспособляемость
- •Знание пределов своих возможностей
- •Принципы групповой работы
- •Коллективное владение кодом
- •Нормы кодирования
- •Определите, что считать успехом
- •Установите ответственность
- •Избегайте истощения
- •Жизненный цикл команды
- •Создание команды
- •Рост команды
- •Групповая работа
- •Роспуск команды
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Управление версиями исходного кода
- •Контроль версий
- •Контроль доступа
- •Работа с хранилищем
- •Пусть растут деревья
- •Краткая история систем контроля за исходным кодом
- •Управление конфигурацией
- •Резервное копирование
- •Выпуск исходного кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Что же это такое, конкретно?
- •Типы спецификаций
- •Спецификация требований
- •Функциональная спецификация
- •Спецификация системной архитектуры
- •Спецификация интерфейса пользователя
- •Проектная спецификация
- •Спецификация тестирования
- •Что должны содержать спецификации?
- •Процесс составления спецификаций
- •Почему мы не пишем спецификации?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Когда проводить рецензирование?
- •Нужно ли рецензировать
- •Какой код рецензировать
- •Проведение рецензирования кода
- •Рецензирование на собраниях
- •Интеграционное рецензирование
- •Пересмотрите свое отношение
- •Позиция автора
- •Позиция рецензента
- •Идеальный код
- •За пределами рецензирования кода
- •Резюме
- •Контрольный список
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Выстрел в темноте
- •Почему трудно делать оценки?
- •Под давлением
- •Практические способы оценки
- •Игры с планами
- •Не отставай!
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Стили программирования
- •Структурное программирование
- •Функциональное программирование
- •Логическое программирование
- •Рецепты: как и что
- •Процессы разработки
- •Каскадная модель
- •SSADM и PRINCE
- •Создание прототипов
- •Итеративная и инкрементная разработка
- •Спиральная модель
- •Другие процессы разработки
- •Спасибо, хватит!
- •Выбор процесса
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 23. За гранью возможного
- •Программирование приложений
- •Коробочные продукты
- •Заказные приложения
- •Программирование игр
- •Системное программирование
- •Встроенное программное обеспечение
- •Программирование масштаба предприятия
- •Численное программирование
- •И что дальше?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 24. Что дальше?
- •Но что же дальше?
- •Ответы и обсуждение
- •Глава 1. Держим оборону
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Вопросы для размышления
- •Глава 17. Вместе мы – сила
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 20. Рецензия на отстрел
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Вопросы для размышления
- •Глава 23. За гранью возможного
- •Вопросы для размышления
- •Вопросы личного характера
- •Библиография
- •Алфавитный указатель
Какую работу нужно написать?

Архитектурные стили |
351 |
Архитектура определяет важнейшие компоненты системы и их взаимодей& ствие. Она не описывает их устройство.
В спецификации архитектуры приводятся принятые конструктивные решения и дается обоснование, почему тот или иной подход получил предпочтение перед возможными альтернативами. Нет необходимости подробно рассматривать эти альтернативы – достаточно обосновать сделанный выбор и показать, что он был вполне обдуманным. В специ% фикации должна быть правильно определена главная задача системы; например, высокая производительность и легкость расширения кон% фликтуют между собой и требуют различных архитектурных решений.
Хорошая архитектура оставляет пространство для маневра; она позво% ляет изменить свое решение. В ней может быть определено, что компо% ненты сторонних разработчиков будут заключены в оболочки абстракт% ных интерфейсов, а потому не составит труда заменить одну их версию другой. Можно предложить технологии, позволяющие выбирать между различными реализациями во время развертывания. По мере того как проект набирает силу, становится ясным, какие варианты реализации нужно выбрать, что не всегда очевидно на ранних стадиях. Удачная ар% хитектура должна быть гибкой, чтобы быстро менять решения во время начальной неопределенности. Архитектура – первая точка, где проис% ходит уравновешивание противодействующих сил; она показывает, ка% кие уступки мы делаем в пользу одного качества и в ущерб другому.
Хорошая архитектура оставляет пространство для маневра, расширения, модификации. Но ее общность не переходит разумных границ.
Архитектура должна быть ясной и недвусмысленной. Предпочтитель% нее хорошо знакомые архитектурные стили и известные структуры (подробнее о них см. в следующем разделе). Архитектура должна быть простой для понимания и легкой для работы.
Как всякий хороший проект, хорошая архитектура должна обладать определенной эстетической привлекательностью, вызывающей к ней
доверие.
Архитектурные стили
Форму в архитектуре определяет функция.
Луис Генри Салливен
Подобно тому как огромный готический собор, причудливая виктори% анская часовня, внушительный квартал высотных зданий и общест% венный туалет 70%х представляют собой различные архитектурные стили, существует ряд признанных стилей в архитектуре программно% го обеспечения, в соответствии с которыми может строиться система.

352 |
Глава 14. Программная архитектура |
Выбор стиля может быть обусловлен многими причинами, не всегда вескими. Например, он может осуществляться исходя из прочных тех% нологических обоснований, прежнего опыта работы архитектора и да% же соображений моды. Каждой архитектуре свойственен определен% ный набор свойств:
•Устойчивость к изменениям в представлении данных, в алгоритмах и необходимых функциях
•Способ разделения и соединения модулей
•Удобопонятность
•Приспосабливаемость к требованиям производительности
•Возможность повторного использования компонент
На практике в рамках одной системы встречается смесь разных сти% лей. В одних частях системы обработка данных может осуществляться с помощью каналов и фильтров, в других частях может применяться компонентная архитектура.
Разберитесь с основными архитектурными стилями и оцените их достоин& ства и недостатки. Это поможет вам более благожелательно относиться к программным продуктам, с которыми вы столкнетесь, и правильно проек& тировать системы.
В последующих разделах описываются некоторые популярные архи% тектурные стили и сравниваются с неким «макаронным» стилем.
Без архитектуры
Архитектура у системы есть всегда, но, как в моем проекте «Лондонская подзем% ка», она может быть незапланированной. Очень скоро такое положение дел оказы% вается ярмом для команды разработчи% ков. В получаемом при этом программном продукте царит полная неразбериха.
Архитектура
в «макаронном» стиле:
Спагетти
Бесформенная, неуправляемая масса.
Определить архитектуру необходимо, если вы собираетесь создать хо% роший программный продукт. Без планирования архитектуры вы за% ранее обрекаете свою разработку на гибель.
Многоуровневая архитектура
Это, вероятно, наиболее употребитель% ный стиль в концептуальных представле% ниях. Он описывает систему в виде иерар% хических уровней с помощью конструк% тивных элементов. Разобраться в такой модели очень легко; даже неспециалист быстро схватит ее идею.
Архитектура
в «макаронном» стиле:
Лазанья
Несколько отдельных слоев, расположенных друг над другом.

Архитектурные стили |
353 |
Каждая компонента представлена одним блоком в стопке. Расположе% ние их в стопке показывает, что где находится, как связаны между со% бой компоненты и каковы возможности одних компонент «видеть» другие. Блоки могут размещаться бок о бок на одном уровне и иметь высоту, охватывающую два уровня. Известным примером служит се% миуровневая эталонная модель OSI для систем сетевой связи (ISO 84). Интереснее семиуровневая модель бисквита «Гудлифс», показанная на рис. 14.1.
На нижнем уровне стека располагается аппаратный интерфейс, если система действительно взаимодействует с физическими устройствами.
Вдругих случаях этот уровень остается за базовыми сервисами, на% пример ОС или промежуточными технологиями типа CORBA. Самый верхний уровень, вероятно, займет интерфейс, с которым взаимодей% ствует пользователь. Подымаясь вверх по стеку, мы удаляемся от ап% паратного уровня, успешно изолируясь от него промежуточными сло% ями, подобно крыше дома, которую мало волнуют магматические про% цессы в ядре Земли.
Влюбой момент можно выкинуть находящиеся внизу уровни и вста% вить новую реализацию низлежащего уровня – система продолжит ра% боту, как прежде. Это важный момент: он означает, что вы можете вы% полнять один и тот же код C++ на любой платформе, поддерживаю% щей вашу среду C++. Можно перейти на другую аппаратную платфор% му, не меняя код приложения, – уровень ОС (например) переварит все технические отличия. Удобно.
Верхние уровни пользуются открытыми интерфейсами уровней, нахо% дящихся непосредственно под ними. Смогут ли они пользоваться от% крытыми интерфейсами еще более глубоко расположенных уровней, зависит от того, как вы определите уровни. Иногда диаграмму подра% батывают так, чтобы она отражала такую возможность, как в случае
Миндаль |
|
Брызги шоколада |
|
|
|
Сливки
Заварной крем
Малиновое желе
Кусочки фруктов
Херес
Бисквит
Чашка
Рис. 14.1. Семиуровневая эталонная модель бисквита «Гудлифс»

354 |
Глава 14. Программная архитектура |
блока хереса в стеке бисквита. Могут ли взаимодействовать между со% бой компоненты одного и того же уровня, тоже строго не определено. Определенно нельзя пользоваться тем, что находится на более высоком уровне; если нарушено это правило, значит, у вас не многоуровневая архитектура, а бессмысленная диаграмма, изображенная в виде стека.
Как можно видеть, большинство диаграмм уровней носит неформаль% ный характер. Относительный размер и положение прямоугольников свидетельствуют о важности тех или иных компонент, и этого обычно достаточно для общего обзора. Соединения между компонентами явно не показаны, а методы связи несущественны. (Однако они могут быть важнейшим архитектурным фактором для эффективности системы – не станете же вы пересылать гигабайты данных через последователь% ный порт RS232.)
Архитектура с каналами и фильтрами
Эта архитектура моделирует логический поток данных через систему. Она реализу% ется в виде цепочки последовательно дей% ствующих модулей, каждый из которых читает некоторые данные, обрабатывает их и выдает дальше наружу. В начале це% почки находится генератор данных (это может быть интерфейс пользователя или
некое аппаратное устройство). В конце находится устройство приема данных (например, дисплей компьютера или журнальный файл). Это старая игра в испорченный телефон, только теперь в цифровом виде. Данные проходят по каналу, встречая по дороге различные фильтры. Преобразования обычно носят нарастающий характер; каждый фильтр осуществляет одну простую операцию и не стремится сохранять свое состояние.
Архитектура каналов и фильтров требует четкого определения струк% туры данных, которые проходят через фильтры; неявно возникают на% кладные расходы в связи с повторяющимся кодированием выходных данных для передачи по каналу и обратным анализом их на входе ка% ждого фильтра. По этим причинам поток данных обычно организован очень просто – в формате простого текста.
В такой архитектуре легко добавлять новые функции путем включе% ния в канал нового фильтра. Главный ее недостаток – обработка оши% бок. Когда в приемнике данных обнаруживается проблема, трудно узнать, в каком месте канала лежит ее источник. Передавать коды ошибок по цепочке затруднительно – их нужно дополнительно коди% ровать и сложно организовать единообразную обработку в нескольких отдельных модулях. Для ошибок фильтры могут использовать отдель% ный канал (например, stderr), но сообщения об ошибках могут легко перемешаться.