
- •2. Структурное программирование. Проектирование сверху вниз. Модульное программирование. Структурное кодирование
- •4. Функции. Компактность. Правило одной операции. Опасность смешения уровней абстракции
- •5. Функции. Правило понижения. Паттерн «Абстрактная фабрика» и использование оператора switch
- •6. Аргументы функций. Приемлемое количество и качество аргументов. Побочные эффекты в функциях. Примеры
- •7. Комментарии. Основные правила написания хороших комментариев. Комментарии todo.
- •8. Комментарии. Основные признаки плохих комментариев. Примеры.
- •9. Форматирование исходного кода. Цель форматирования. Вертикальное разделение концепций, вертикальное сжатие. Вертикальное расстояние
- •10. Форматирование исходного кода. Цель форматирования. Горизонтальное форматирование. Горизонтальное разделение и сжатие. Отступы
- •11. Объекты и структуры данных. Отличия процедурного и объектно-ориентированного кода. Случаи применения
- •12. Закон Деметры. Опасность построения гибридов объектов и структур данных. Объекты передачи данных и активные записи
- •13. Обработка ошибок. Исключения и коды ошибок. Использование паттерна «Особый случай».
- •14. Использование стороннего программного кода. Учебные тесты как инструмент исследования и анализа граничного кода.
- •16. Класс. Размеры класса. Принцип единой ответственности (srp).
- •17. Понятие связности класса. Влияние связности на размер классов.
- •18. Структурирование класса с учетом его изменений. Принципы проектирования классов в ооп.
- •19. Понятие эффективности программы. Выбор между эффективностью и удобочитаемостью. Оптимизирующие компиляторы.
- •20. Методология разработки через тестирование (tdd). Последовательность этапов разработки при использовании методологии tdd. Три закона tdd.
- •21. Тестирование как важный этап процесса разработки по. Чистота тестов. Тесты как средство обеспечения изменений. Правило «одна концепция на тест».
- •22. Экономические аспекты процесса тестирования. Тестирование методами «черного» и «белого» ящика. Невозможность исчерпывающего тестирования.
- •23. Основные принципы тестирования программного обеспечения.
- •24. Понятие отладки. Отличие между отладкой и тестированием. Средства отладки. Защитное программирование
- •25. Понятие отладки. Основные принципы отладки. Принципы локализации ошибок. Принципы устранения ошибок.
- •26. Понятие отладки. Основные подходы к отладке программ. Методы «грубой силы», индуктивная отладка, дедуктивная отладка, обратная трассировка, отладка тестированием.
- •28. Понятие правильности программ. Доказательство правильности программ. Правильность программ
- •29. Типы разложения вычислений (сочленение, выбор, повторение).
- •If условие then оператор 1 else оператор 2
- •30. Неоднозначность определения программы. Проблема сравнения программ.
- •32. Понятие рефакторинга. Рефакторинги «Согласование различий», «Миграция данных», «Выделение метода».
- •33. Понятие рефакторинга. Рефакторинги «Встраивание метода», «Выделение интерфейса», «Перемещение метода».
- •Inline method (встраивание метода)
- •34. Понятие рефакторинга. Рефакторинги «Метод в объект», «Добавление параметра», «Параметр метода в параметр конструктора».
13. Обработка ошибок. Исключения и коды ошибок. Использование паттерна «Особый случай».
Обработка ошибок — одна из тех рутинных вещей, которыми нам всем приходится заниматься при программировании.
ИСПОЛЬЗУЙТЕ ИСКЛЮЧЕНИЯ ВМЕСТО КОДОВ ОШИБОК
В далеком прошлом многие языки программирования не поддерживали механизма обработки исключений. Программа либо устанавливала флаг ошибки, либо возвращала код, который проверялся вызывающей стороной.
НАЧНИТЕ С НАПИСАНИЯ КОМАНДЫ TRY-CATCH-FINALLY
Размещая код в секции try команды try-catch-final 1у, вы утверждаете, что выполнение программы может прерваться в любой точке, а затем продолжиться в секции catch.
Секция catch должна оставить программу в целостном состоянии, что бы и произошло в секции try. По этой причине написание кода, который может инициировать исключения, рекомендуется начинать с конструкции try-catch-final 1у.
ОПРЕДЕЛИТЕ НОРМАЛЬНЫЙ ПУТЬ ВЫПОЛНЕНИЯ
Паттерн ОСОБЫЙ СЛУЧАЙ [Fowler]. Программист создает класс или настраивает объект так, чтобы он обрабатывал особый случай за него. Это позволяет избежать обработки исключительного поведения в клиентском коде. Все необходимое поведение инкапсулируется в объекте особого случая.
14. Использование стороннего программного кода. Учебные тесты как инструмент исследования и анализа граничного кода.
ИСПОЛЬЗОВАНИЕ СТОРОННЕГО КОДА
Между поставщиком и пользователем интерфейса существует естественная напряженность. Поставщики сторонних пакетов и инфраструктур стремятся к универсальности, чтобы их продукты работали в разных средах и были обращены к широкой аудитории. С другой стороны, пользователи желают получить интерфейс, специализирующийся на их конкретных потребностях. Эта напряженность приводит к появлению проблем на границах наших систем.
ИССЛЕДОВАНИЕ И АНАЛИЗ ГРАНИЦ
Сторонний код помогает нам реализовать больше функциональности за меньшее время. С чего начинать, если мы хотим использовать сторонний пакет? Тестирование чужого кода не входит в наши обязанности, но, возможно, написание тестов для стороннего кода, используемого в наших продуктах, в наших же интересах.
Допустим, вам не ясно, как использовать стороннюю библиотеку. Можно потратить день-два (или более) на чтение документации и принятие решений о том, как работать с библиотекой. Затем вы пишете код, использующий стороннюю библиотеку, и смотрите, делает ли он то, что ожидалось. Далее вы, скорее всего, погрязнете в долгих сеансах отладки, пытаясь разобраться, в чьем коде возникают ошибки - в стороннем или в вашем собственном.
Изучение чужого кода - непростая задача. Интеграция чужого кода тоже сложна. Одновременное решение обоих задач создает двойные сложности. А что, если пойти по другому пути? Вместо того чтобы экспериментировать и опробовать новую библиотеку в коде продукта, можно написать тесты, проверяющие наше понимание стороннего кода. Джим Ньюкирк (Jim Newkirk) называет такие тесты «учебными тестами» [BeckTDD, pp. 136-137].
В учебных тестах мы вызываем методы стороннего API в том виде, в котором намереваемся использовать их в своем приложении. Фактически выполняется контролируемый эксперимент, проверяющий наше понимание стороннего API. Основное внимание в тестах направлено на то, чего мы хотим добиться при помощи API.
УЧЕБНЫЕ ТЕСТЫ: ВЫГОДНЕЕ, ЧЕМ БЕСПЛАТНО
Учебные тесты не стоят ничего. API все равно приходится изучать, а написание тестов является простым способом получения необходимой информации, в изоляции от рабочего кода. Учебные тесты были точно поставленными экспериментами, которые помогли нам расширить границы своего понимания. Учебные тесты не просто бесплатны - они приносят дополнительную прибыль. При выходе новых версий сторонних пакетов вы сможете провести учебные тесты и выяснить, не изменилось ли поведение пакета.
Учебные тесты позволяют убедиться в том, что сторонние пакеты, используемые в коде, работают именно так, как мы ожидаем. Нет никаких гарантий, что сторонний код, интегрированный в наши приложения, всегда будет сохранять совместимость. Например, авторы могут изменить код в соответствии с какими-то новыми потребностями. Изменения также могут происходить из-за исправления ошибок и добавления новых возможностей. Выход каждой новой версии сопряжен с новым риском. Если в стороннем пакете появятся изменения, несовместимые с нашими тестами, мы сразу узнаем об этом.
Впрочем, независимо от того, нужна ли вам учебная информация, получаемая в ходе тестирования, в системе должна существовать четкая граница, которая поддерживается группой исходящих тестов, использующих интерфейс но аналогии с кодом продукта. Без граничных тестов, упрощающих процесс миграции, у нас появляются причины задержаться на старой версии дольше необходимого.
15. ИСПОЛЬЗОВАНИЕ НЕСУЩЕСТВУЮЩЕГО КОДА
В коде часто встречаются места, в которых мы не располагаем полной информацией. Иногда то, что находится на другой стороне границы, остается неизвестным (по крайней мере в данный момент). Иногда мы намеренно не желаем заглядывать дальше границы.
Несколько лет назад я работал в группе, занимавшейся разработкой программного обеспечения для системы радиосвязи. В нашем продукте была подсистема «Передатчик», о которой мы почти ничего не знали, а люди, ответственные за разработку этой подсистемы, еще не дошли до определения своего интерфейса. Мы не хотели простаивать и поэтому начали работать подальше от неизвестной части кода.
Мы неплохо представляли себе, где заканчивалась наша зона ответственности и начиналась чужая территория. В ходе работы мы иногда наталкивались на границу. Хотя туманы и облака незнания скрывали пейзаж за границей, в ходе работы мы начали понимать, каким должен быть граничный интерфейс. Передатчику должны были отдаваться распоряжения следующего вида:
Настроить передатчик на заданную частоту и отправить аналоговое представление данных, поступающих из следующего потока.
Мы тогда понятия не имели, как это будет делаться, потому что API еще не был спроектирован. Поэтому подробности было решено отложить на будущее.
Чтобы не останавливать работу, мы определили собственный интерфейс с броским именем Transmitter. Интерфейс содержал метод transmit, которому при вызове передавались частота и поток данных. Это был тот интерфейс, который нам хотелось бы иметь.
У этого интерфейса было одно важное достоинство: он находился под нашим контролем. В результате клиентский код лучше читался, а мы в своей работе могли сосредоточиться на том, чего стремились добиться.
На рис. 8.2 мы видим, что классы CommunicationsControllег отделены от API передатчика (который находился вне нашего контроля и оставался неопределенным). Использование конкретного интерфейса нашего приложения позволило сохранить чистоту и выразительность кода CommunicationsControllег. После того как другая группа определила API передатчика, мы написали класс Transmi tterAdapter для «наведения мостов». АДАПТЕР инкапсулировал взаимодействие с API и создавал единое место для внесения изменений в случае развития API.
Такая архитектура также создает в коде очень удобный «стык» для тестирования. Используя подходящий FakeTransmitter, мы можем тестировать классы CommunicationsController. Кроме того, сразу же после появления TransmitterAPI можно создать граничные тесты для проверки правильности использования API.