
- •1. Предпосылки возникновения методологии структурного программирования. Основные принципы структурного программирования. Теорема Бёма-Якопини.
- •2. Структурное программирование. Проектирование сверху вниз. Модульное программирование. Структурное кодирование
- •4. Функции. Компактность. Правило одной операции. Опасность смешения уровней абстракции
- •5. Функции. Правило понижения. Паттерн «Абстрактная фабрика» и использование оператора switch
- •6. Аргументы функций. Приемлемое количество и качество аргументов. Побочные эффекты в функциях. Примеры
- •7. Комментарии. Основные правила написания хороших комментариев. Комментарии todo.
- •8. Комментарии. Основные признаки плохих комментариев. Примеры.
- •9. Форматирование исходного кода. Цель форматирования. Вертикальное разделение концепций, вертикальное сжатие. Вертикальное расстояние
- •10. Форматирование исходного кода. Цель форматирования. Горизонтальное форматирование. Горизонтальное разделение и сжатие. Отступы
- •11. Объекты и структуры данных. Отличия процедурного и объектно-ориентированного кода. Случаи применения
- •12. Закон Деметры. Опасность построения гибридов объектов и структур данных. Объекты передачи данных и активные записи
- •13. Обработка ошибок. Исключения и коды ошибок. Использование паттерна «Особый случай».
- •14. Использование стороннего программного кода. Учебные тесты как инструмент исследования и анализа граничного кода.
- •15. Проблемы использования стороннего программного кода. Применение паттерна «Адаптер» для организации взаимодействия с недоступным кодом.
- •16. Класс. Размеры класса. Принцип единой ответственности (srp).
- •17. Понятие связности класса. Влияние связности на размер классов.
- •18. Структурирование класса с учетом его изменений. Принципы проектирования классов в ооп.
- •19. Понятие эффективности программы. Выбор между эффективностью и удобочитаемостью. Оптимизирующие компиляторы.
- •20. Методология разработки через тестирование (tdd). Последовательность этапов разработки при использовании методологии tdd. Три закона tdd.
- •21. Тестирование как важный этап процесса разработки по. Чистота тестов. Тесты как средство обеспечения изменений. Правило «одна концепция на тест».
- •22. Экономические аспекты процесса тестирования. Тестирование методами «черного» и «белого» ящика. Невозможность исчерпывающего тестирования.
- •23. Основные принципы тестирования программного обеспечения.
- •24. Понятие отладки. Отличие между отладкой и тестированием. Средства отладки. Защитное программирование
- •25. Понятие отладки. Основные принципы отладки. Принципы локализации ошибок. Принципы устранения ошибок.
- •26. Понятие отладки. Основные подходы к отладке программ. Методы «грубой силы», индуктивная отладка, дедуктивная отладка, обратная трассировка, отладка тестированием.
- •27. Проблема ограниченности вычислительных систем. Возможности преодоления некоторых типов ограничений.
- •28. Понятие правильности программ. Доказательство правильности программ. Правильность программ
- •29. Типы разложения вычислений (сочленение, выбор, повторение).
- •If условие then оператор 1 else оператор 2
- •30. Неоднозначность определения программы. Проблема сравнения программ.
- •32. Понятие рефакторинга. Рефакторинги «Согласование различий», «Миграция данных», «Выделение метода».
- •33. Понятие рефакторинга. Рефакторинги «Встраивание метода», «Выделение интерфейса», «Перемещение метода».
- •Inline method (встраивание метода)
- •34. Понятие рефакторинга. Рефакторинги «Метод в объект», «Добавление параметра», «Параметр метода в параметр конструктора».
6. Аргументы функций. Приемлемое количество и качество аргументов. Побочные эффекты в функциях. Примеры
АРГУМЕНТЫ ФУНКЦИЙ
В идеальном случае количество аргументов функции равно нулю (нуль-арная функция). Далее следуют функции с одним аргументом (унарные) и с двумя аргументами (бинарные). Функций с тремя аргументами (тернарных) следует по возможности избегать. Необходимость функций с большим количеством аргументов (полиарных) должна быть подкреплена очень вескими доводами — и все равно такие функции лучше не использовать.
Аргументы усложняют функции и лишают их значительной части концептуальной мощи.
Аргументы создают еще больше проблем с точки зрения тестирования. Только представьте, как трудно составить все тестовые сценарии, проверяющие правильность работы кода со всеми комбинациями аргументов. Если аргументов нет — задача тривиальна. При одном аргументе все обходится без особых сложностей. С двумя аргументами ситуация усложняется. Если же аргументов больше двух, задача тестирования всех возможных комбинаций выглядит все более устрашающе.
Выходные аргументы запутывают ситуацию еще быстрее, чем входные. Читая код функции, мы обычно предполагаем, что функция получает информацию в аргументах, и выдает ее в возвращаемом значении. Как правило, никто не ожидает, что функция будет возвращать информацию в аргументах. Таким образом, выходные аргументы часто заставляют нас браться за чтение функции заново.
Если уж обойтись без аргументов никак не удается, постарайтесь хотя бы ограничиться одним входным аргументом.
СТАНДАРТНЫЕ УНАРНЫЕ ФОРМЫ
Существует два очень распространенных случая вызова функции с одним аргументом. Первая — проверка некоторого условия, связанного с аргументом, как в вызове boolean fileExists("MyFile"). Вторая — обработка аргумента, его преобразование и возвращение. Например, вызов InputStream fi 1е0реп( "MyFi le") преобразует имя файла в формате String в возвращаемое значение InputStream.
Не допускается испытание так называемых выходных аргументов. Большое количество аргументов затрудняет тестирование функций, так как необходимо проверять большое количество случаев.
Не следует использовать так называемые аргументы флаги. Их наличие явно указывает наличие у функций более одного действия. Передача логического значения функции — воистину ужасная привычка. Она немедленно усложняет сигнатуру метода, громко провозглашая, что функция выполняет более одной операции. При истинном значении флага выполняется одна операция, а при ложном — другая!
БИНАРНЫЕ ФУНКЦИИ
Функцию с двумя аргументами понять сложнее, чем унарную функцию. Например, вызов writeField(name) выглядит более доступно, чем writeField(outputStream.name). Хотя смысл обеих форм понятен, первая форма просто проскальзывает под нашим взглядом, моментально раскрывая свой смысл. Во второй форме приходится сделать непродолжительную паузу, пока вы не поймете, что первый параметр должен игнорироваться. И конечно, это в конечном итоге создает проблемы, потому что никакие части кода игнорироваться не должны. Именно в проигнорированных частях чаще всего скрываются ошибки.
ТЕРНАРНЫЕ ФУНКЦИИ
Разобраться в функции с тремя аргументами значительно сложнее, чем в бинарной функции. Проблемы соблюдения порядка аргументов, приостановки чтения и игнорирования увеличиваются более чем вдвое. Я рекомендую очень хорошо подумать, прежде чем создавать тернарную функцию.
Для примера возьмем стандартную перегруженную версию assertEquals с тремя аргументами: assertEquals (message, expected, actual). Сколько раз вы читали значение message и думали, что перед вами expected?
ИЗБАВЬТЕСЬ ОТ ПОБОЧНЫХ ЭФФЕКТОВ
Побочные эффекты суть ложь. Ваша функция обещает делать что-то одно, но делает что-то другое, скрытое от пользователя. Иногда она вносит неожиданные изменения в переменные своего класса — скажем, присваивает им значения параметров, переданных функции, или глобальных переменных системы.
Для примера возьмем безвредную на первый взгляд функцию из листинга 3.6. Функция использует стандартный алгоритм для проверки пары «имя пользователя/пароль». Она возвращает true в случае совпадения или false при возникновении проблем. Но у функции также имеется побочный эффект. Сможете ли вы обнаружить его?
Листинг 3.6. UserValidator.java
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password)
{
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPasswordO;
String phrase = cryptographer.decrypt(codedPhrase. password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
Разумеется, побочным эффектом является вызов Session.initialize(). Имя checkPassword сообщает, что функция проверяет пароль. Оно ничего не говорит о том, что функция инициализирует сеанс. Таким образом, тот, кто поверит имени функции, рискует потерять текущие сеансовые данные, когда он решит проверить данные пользователя.
Побочный эффект создает временную привязку. А именно, функция checkPassword может вызываться только в определенные моменты времени (когда инициализация сеанса может быть выполнена безопасно). Несвоевременный вызов может привести к непреднамеренной потере сеансовых данных. Временные привязки создают массу проблем, особенно когда они прячутся в побочных эффектах. Если без временной привязки не обойтись, этот факт должен быть четко оговорен в имени функции. В нашем примере функцию можно было бы переименовать в checkPasswordAndlnitial izeSession, хотя это безусловно нарушает правило «одной операции».