- •Введение
- •От издательства
- •Глава 1. Организация процесса конструирования
- •Определение технологии конструирования программного обеспечения
- •Классический жизненный цикл
- •Макетирование
- •Стратегии конструирования по
- •Инкрементная модель
- •Быстрая разработка приложений
- •Спиральная модель
- •Компонентно-ориентированная модель
- •Тяжеловесные и облегченные процессы
- •Модели качества процессов конструирования
- •Контрольные вопросы
- •Глава 2. Руководство программным проектом
- •Процесс руководства проектом
- •Начало проекта
- •Измерения, меры и метрики
- •Планирование проектных задач
- •Размерно-ориентированные метрики
- •Функционально-ориентированные метрики
- •Выполнение оценки в ходе руководства проектом
- •Выполнение оценки проекта на основе loc- и fp-метрик
- •Конструктивная модель стоимости
- •Модель композиции приложения
- •Модель раннего этапа проектирования
- •Модель этапа постархитектуры
- •Предварительная оценка программного проекта
- •Анализ чувствительности программного проекта
- •Сценарий понижения зарплаты
- •Сценарий наращивания памяти
- •Сценарий использования нового микропроцессора
- •Сценарий уменьшения средств на завершение проекта
- •Контрольные вопросы
- •Глава 3. Классические методы анализа
- •Структурный анализ
- •Диаграммы потоков данных
- •Описание потоков данных и процессов
- •Расширения для систем реального времени
- •Расширение возможностей управления
- •Модель системы регулирования давления космического корабля
- •Методы анализа, ориентированные на структуры данных
- •Метод анализа Джексона
- •Методика Джексона
- •Шаг объект-действие
- •Шаг объект-структура
- •Шаг начального моделирования
- •Контрольные вопросы
- •Глава 4. Основы проектирования программных систем
- •Особенности процесса синтеза программных систем
- •Особенности этапа проектирования
- •Структурирование системы
- •Моделирование управления
- •Декомпозиция подсистем на модули
- •Модульность
- •Информационная закрытость
- •Связность модуля
- •Функциональная связность
- •Информационная связность
- •Коммуникативная связность
- •Процедурная связность
- •Временная связность
- •Логическая связность
- •Связность по совпадению
- •Определение связности модуля
- •Сцепление модулей
- •Сложность программной системы
- •Характеристики иерархической структуры программной системы
- •Контрольные вопросы
- •Глава 5. Классические методы проектирования
- •Метод структурного проектирования
- •Типы информационных потоков
- •Проектирование для потока данных типа «преобразование»
- •Диаграмма потоков данных пдд
- •Проектирование для потока данных типа «запрос»
- •Диаграмма потоков данных
- •Метод проектирования Джексона
- •Доопределение функций
- •Учет системного времени
- •Контрольные вопросы
- •Глава 6. Структурное тестирование программного обеспечения
- •Основные понятия и принципы тестирования по
- •Тестирование «черного ящика»
- •Тестирование «белого ящика»
- •Особенности тестирования «белого ящика»
- •Способ тестирования базового пути
- •Потоковый граф
- •Цикломатическая сложность
- •Шаги способа тестирования базового пути
- •Способы тестирования условий
- •Тестирование ветвей и операторов отношений
- •Способ тестирования потоков данных
- •Тестирование циклов
- •Простые циклы
- •Вложенные циклы
- •Объединенные циклы
- •Неструктурированные циклы
- •Контрольные вопросы
- •Глава 7. Функциональное тестирование программного обеспечения
- •Особенности тестирования «черного ящика»
- •Способ разбиения по эквивалентности
- •Способ анализа граничных значений
- •Способ диаграмм причин-следствий
- •Контрольные вопросы
- •Глава 8. Организация процесса тестирования программного обеспечения
- •Методика тестирования программных систем
- •Тестирование элементов
- •Тестирование интеграции
- •Нисходящее тестирование интеграции
- •Восходящее тестирование интеграции
- •Сравнение нисходящего и восходящего тестирования интеграции
- •Тестирование правильности
- •Системное тестирование
- •Тестирование восстановления
- •Тестирование безопасности
- •Стрессовое тестирование
- •Тестирование производительности
- •Искусство отладки
- •Контрольные вопросы
- •Глава 9. Основы объектно-ориентированного представления программных систем
- •Принципы объектно-ориентированного представления программных систем
- •Абстрагирование
- •Инкапсуляция
- •Модульность
- •Иерархическая организация
- •Объекты
- •Общая характеристика объектов
- •Виды отношений между объектами
- •Видимость объектов
- •Агрегация
- •Общая характеристика классов
- •Виды отношений между классами
- •Ассоциации классов
- •Наследование
- •Полиморфизм
- •Агрегация
- •Зависимость
- •Конкретизация
- •Контрольные вопросы
- •Глава 10. Базис языка визуального моделирования
- •Унифицированный язык моделирования
- •Предметы в uml
- •Отношения в uml
- •Диаграммы в uml
- •Механизмы расширения в uml
- •Контрольные вопросы
- •Глава 11. Статические модели объектно-ориентированных программных систем
- •Вершины в диаграммах классов
- •Свойства
- •Операции
- •Организация свойств и операций
- •Множественность
- •Отношения в диаграммах классов
- •Деревья наследования
- •Примеры диаграмм классов
- •Контрольные вопросы
- •Глава 12. Динамические модели объектно-ориентированных программных систем
- •Моделирование поведения программной системы
- •Диаграммы схем состояний
- •Действия в состояниях
- •Условные переходы
- •Вложенные состояния
- •Диаграммы деятельности
- •Диаграммы взаимодействия
- •Диаграммы сотрудничества
- •Диаграммы последовательности
- •Диаграммы Use Case
- •Актеры и элементы Use Case
- •Отношения в диаграммах Use Case
- •Работа с элементами Use Case
- •Спецификация элементов Use Case
- •Главный поток
- •Подпотоки
- •Альтернативные потоки
- •Пример диаграммы Use Case
- •Построение модели требований
- •Кооперации и паттерны
- •Паттерн Наблюдатель
- •Паттерн Компоновщик
- •Паттерн Команда
- •Бизнес-модели
- •Контрольные вопросы
- •Глава 13. Модели реализации объектно-ориентированных программных систем
- •Компонентные диаграммы
- •Компоненты
- •Интерфейсы
- •Компоновка системы
- •Разновидности компонентов
- •Использование компонентных диаграмм
- •Моделирование программного текста системы
- •Моделирование реализации системы
- •Основы компонентной объектной модели
- •Организация интерфейса сом
- •Идентификация интерфейса
- •Описание интерфейса
- •Реализация интерфейса
- •Unknown — базовый интерфейс com
- •Серверы сом-объектов
- •Преимущества com
- •Работа с сом-объектами
- •Создание сом-объектов
- •IClassFactory :: Createlnstance (iid a); 2 — фабрика класса создает сом-объект и получает
- •Повторное использование сом-объектов
- •Маршалинг
- •Диаграммы размещения
- •Использование диаграмм размещения
- •Контрольные вопросы
- •Глава 14. Метрики объектно-ориентированных программных систем
- •Метрические особенности объектно-ориентированных программных систем
- •Локализация
- •Инкапсуляция
- •Информационная закрытость
- •Наследование
- •Абстракция
- •Эволюция мер связи для объектно-ориентированных программных систем
- •Связность объектов
- •Метрики связности по данным
- •Метрики связности по методам
- •Сцепление объектов
- •Зависимость изменения между классами
- •Локальность данных
- •Набор метрик Чидамбера и Кемерера
- •Метрика 1: Взвешенные методы на класс wmc (Weighted Methods Per Class)
- •Метрика 2: Высота дерева наследования dit (Depth of Inheritance Tree)
- •Метрика 3: Количество детей noc (Number of children)
- •Метрика 4: Сцепление между классами объектов сво (Coupling between object classes)
- •Метрика 5: Отклик для класса rfc (Response For a Class)
- •Метрика 6: Недостаток связности в методах lсom (Lack of Cohesion in Methods)
- •Использование метрик Чидамбера-Кемерера
- •Метрики Лоренца и Кидда
- •Метрики, ориентированные на классы
- •Метрика 1: Размер класса cs (Class Size)
- •Метрика 2: Количество операций, переопределяемых подклассом, noo
- •Метрика 3: Количество операций, добавленных подклассом, noa
- •Метрика 4: Индекс специализации si (Specialization Index)
- •Операционно-ориентированные метрики
- •Метрика 5: Средний размер операции osavg (Average Operation Size)
- •Метрика 6: Сложность операции ос (Operation Complexity
- •Метрика 7: Среднее количество параметров на операцию npavg
- •Метрики для оо-проектов
- •Метрика 8: Количество описаний сценариев nss (Number of Scenario Scripts)
- •Метрика 9: Количество ключевых классов nkc (Number of Key Classes)
- •Метрика 10: Количество подсистем nsub (NumberofSuBsystem)
- •Набор метрик Фернандо Абреу
- •Метрика 1: Фактор закрытости метода mhf (Method Hiding Factor)
- •Метрика 2: Фактор закрытости свойства ahf (Attribute Hiding Factor)
- •Метрика 3: Фактор наследования метода mif (Method Inheritance Factor)
- •Метрика 4: Фактор наследования свойства aif (Attribute Inheritance Factor)
- •Метрика 5: Фактор полиморфизма pof (Polymorphism Factor)
- •Метрика 6: Фактор сцепления cof (Coupling Factor)
- •Метрики для объектно-ориентированного тестирования
- •Метрики инкапсуляции
- •Метрика 1: Недостаток связности в методах lcom
- •Метрика 2: Процент публичных и защищенных pap (Percent Public and Protected)
- •Метрика 3: Публичный доступ к компонентным данным pad (Public Access to Data members)
- •Метрики наследования
- •Метрики полиморфизма
- •Контрольные вопросы
- •Глава 15. Унифицированный процесс разработки объектно-ориентированных пс
- •Эволюционно-инкрементная организация жизненного цикла разработки
- •Этапы и итерации
- •Рабочие потоки процесса
- •Технические артефакты
- •Управление риском
- •Идентификация риска
- •Анализ риска
- •Ранжирование риска
- •Планирование управления риском
- •Разрешение и наблюдение риска
- •Этапы унифицированного процесса разработки
- •Этап начало (Inception)
- •Этап развитие (Elaboration)
- •Этап конструирование (Construction)
- •Этап переход (Transition)
- •Оценка качества проектирования
- •Этап развитие
- •Этап конструирование
- •Пример объектно-ориентированной разработки
- •Этап начало
- •Идентификация актеров
- •Идентификация элементов Use Case
- •Описания элементов Use Case
- •Этап развитие
- •Сценарии для элемента Use Case Управление окнами
- •Развитие описания элемента Use Case Использование окон
- •Диаграммы последовательности
- •15.9. Диаграмма последовательности Уничтожение окна
- •Создание классов
- •Планирование итераций конструирования
- •Этап конструирование
- •Итерация 1 — реализация сценариев элемента Use Case Управление окнами
- •Итерация 2 — реализация сценариев элемента Use Case Использование окон
- •Итерация 3 — разработка диалогового окна
- •Разработка в стиле экстремального программирования
- •Элемент хр-разработки
- •Коллективное владение кодом
- •Взаимодействие с заказчиком
- •Стоимость изменения и проектирование
- •Контрольные вопросы
- •Глава 16. Объектно-ориентированное тестирование
- •Расширение области применения объектно-ориентированного тестирования
- •Изменение методики при объектно-ориентированном тестировании
- •Особенности тестирования объектно-ориентированных «модулей»
- •Тестирование объектно-ориентированной интеграции
- •Объектно-ориентированное тестирование правильности
- •Проектирование объектно-ориентированных тестовых вариантов
- •Тестирование, основанное на ошибках
- •Тестирование, основанное на сценариях
- •Тестирование поверхностной и глубинной структуры
- •Способы тестирования содержания класса
- •Стохастическое тестирование класса
- •Тестирование разбиений на уровне классов
- •Способы тестирования взаимодействия классов
- •Стохастическое тестирование
- •Тестирование разбиений
- •Тестирование на основе состояний
- •Предваряющее тестирование при экстремальной разработке
- •Import ПосещениеКафе;
- •V.ПолучитьВес();
- •Контрольные вопросы
- •Глава 17. Автоматизация конструирования визуальной модели программной системы
- •Общая характеристика case-системы Rational Rose
- •Создание диаграммы Use Case
- •Создание диаграммы последовательности
- •Создание диаграммы классов
- •Создание компонентной диаграммы
- •Генерация программного кода
- •Заключение
- •Приложение а. Факторы затрат постархитектурной модели сосомо II
- •Сложность продукта (Product Complexity) cplx
- •Приложение б.Терминология языка uml и унифицированного процесса
- •Приложение в. Основные средства языка программирования Ada 95
- •Типы и объекты данных
- •Текстовый и числовой ввод-вывод
- •Пакеты ввода-вывода
- •Процедуры ввода
- •Процедуры вывода
- •Основные операторы
- •Операторы цикла
- •Основные программные модули
- •Функции
- •Процедуры
- •Производные типы
- •Подтипы
- •Расширяемые типы
- •Список литературы
- •Оглавление
- •Глава 1. Организация процесса конструирования 6
- •Глава 2. Руководство программным проектом 19
- •Глава 3. Классические методы анализа 41
- •Глава 4. Основы проектирования программных систем 52
- •Глава 5. Классические методы проектирования 67
- •Глава 6. Структурное тестирование программного обеспечения 74
- •Глава 7. Функциональное тестирование программного обеспечения 88
- •Глава 8. Организация процесса тестирования программного обеспечения 96
- •Глава 9. Основы объектно-ориентированного представления программных систем 107
- •Глава 10. Базис языка визуального моделирования 124
- •Глава 11. Статические модели объектно-ориентированных программных систем 131
- •Глава 12. Динамические модели объектно-ориентированных программных систем 141
- •Глава 13. Модели реализации объектно-ориентированных программных систем 170
- •Глава 14. Метрики объектно-ориентированных программных систем 190
- •Глава 15. Унифицированный процесс разработки объектно-ориентированных пс 210
- •Глава 16. Объектно-ориентированное тестирование 238
- •Глава 17. Автоматизация конструирования визуальной модели программной системы 263
- •Технологии разработки программного обеспечения: Учебник
- •197110, Санкт-Петербург, Чкаловский пр., 15.
Import ПосещениеКафе;
public class ТестЛакомки extends TestCase
{
public ТестЛакомки (String name)
{
super(name);
}
public void тестСоздатьПосещениеКафе()
{
ПосещениеКафе v = new ПосещениеКафе();
}
}
ПосещениеКафе.java
public class ПосещениеКафе
{
}
Этот код компилируется, тест проходит, и мы готовы добавить необходимую функциональность.
Листинг 16.4. ТестЛакомки.jауа и ПосещениеКафе.jауа
ТестЛакомки.java
import junit.framework.*;
import ПосещениеКафе;
import java.util.Date
public class ТестЛакомки extends TestCase
{
public TecтЛакомки(String name)
{
super(name):
}
public void тестСоздатьПосещениеКафе()
{
Date дата = new Date();
double булочки = 7.0; // 7 булочек
double стоимость = 12.5 * 7;
// цена 1 булочки - 12.5 руб.
double вес = 60.0; // взвешивание лакомки
double дельта = 0.0001; // точность
ПосещениеКафе v =
new ПосещениеКафе(дата, булочки, стоимость, вес);
assertEquals(дата, v.получитьДату( ));
assertEquals(12.5 * 7, v.получитьСтоииость(), дельта);
assertEquals(7.0, v.получитьБулочки(), дельта);
assertEquals(60.0, v.получитьВес(), дельта);
assertEquals(12.5, v.получитьЦену(). дельта);
}
}
ПосещениеКафе.java
import Java.uti1.Date;
public class ПосещениеКафе
{
private Date егоДата;
private double егоБулочки;
private double егоСтоимость;
private double eroBec;
public ПосещениеКафе(Date дата, double булочки,
double стоимость, double вес)
{
егоДата = дата;
егоБулочки = булочки;
егоСтоимость = стоимость;
егоВес = вес;
}
public Date получитьДату() {return егоДата;}
public double получитьБулочки() {return егоБулочки;}
public double получитьСтоимость() {return егоСтоимость;}
public double получитьЦену(){return егоСтоимость/егоБулочки;}
public double получитьВес() {return eroBec;}
}
На этом шаге мы добавили тесты в класс ТестЛакомки, а также добавили методы в класс ПосещениеКафе. Унаследованные методы assertEquals позволяют проводить сравнение ожидаемых и фактических результатов тестирования.
Очевидно, вы удивитесь этому подходу. Неужели нельзя вначале написать весь код класса ПосещениеКафе, а потом создать тесты? Ответ достаточно прост. Написание тестов перед написанием программного кода дает важное преимущество: мы знаем, что весь ранее созданный код компилируется и выполняется. Следовательно, любая ошибка вызывается текущими изменениями, а не более ранним кодом. И значимость этого преимущества усиливается по мере продвижения вперед.
Далее определимся с хранением объектов класса ПосещениеКафе. Очевидно, что свойство егоВес характеризует лакомку. Таким образом, объект ПосещениеКафе записывает часть состояния лакомки па момент посещения кафе. Следовательно, нужно создать объект Лакомка и содержать объекты класса ПосещениеКафе в нем.
Листинг 16.5. ТестЛакомки.java и Лакомка.java
ТестЛакомки.java
import junit.framework.*;
import ПосещениеКафе;
import java.util.Date
public class ТестЛакомки extends TestCase
{
public TecтЛакомки(String name)
{
super(name);
}
…
public void тестСоздатьЛакомку()
{
Лакомка g = new Лакомка();
assertEquals(0, д.получитьЧислоПосещений());
}
}
Лакомка.Java
public class Лакомка
{
public int получитьЧислоПосещений()
{
return 0;
}
}
Листинг 16.5 показывает начальный шаг. Мы написали новую тестовую функцию тестСоздатьЛакомку. Эта функция создает объект класса Лакомка и затем убеждается, что хранимое количество посещений равно 0. Конечно, реализация метода получитьЧислоПосещений неверна, но она обеспечивает прохождение теста. Это позволит нам в будущем выполнить рефакторинг (для улучшения решения).
Введем в класс Лакомку объект-контейнер, хранящий данные о разных посещениях (как элементы списка в массиве изменяемого размера). Для его создания используем класс-контейнер Array List из библиотеки Java 2. В будущем нам потребуются три метода контейнера: add (добавить элемент в контейнер), get (получить элемент из контейнера), size (вернуть количество элементов в контейнере).
Листинг 16.6. ЛАKOMKА.java
import java.util.ArrayList;
public class Лакомка
{
private ArrayList егоПосещения = new ArrayList();
// создание объекта егоПосещения - контейнера посещений
public int получитьЧислоПосещений ()
{
return егоПосещения.size();
// возврат количества элементов в контейнере
// оно равно количеству посещений кафе
}
}
Отметим, что после каждого изменения мы прогоняем все тесты, а не только функцию тестСоздатьЛакомку. Это дает гарантию, что изменения не испортили уже работающий код.
На следующем шаге следует определить, как к Лакомке добавляется посещение кафе. Так будет выглядеть простейший тестовый вариант:
Листинг 16.7. TecтЛакомки.java
public void тестДобавитьПосещение()
{
double булочки = 7.0; // 7 булочек
double стоимость = 12.5 * 7; // цена 1 булочки = 12.5 руб.
double вес = 60.0; // взвешивание лакомки
double дельта = 0.0001; // точность
Лакомка g = new Лакомка();
g.добавитьПосещениеКафе(булочки, стоимость, вес);
assertEquals(1, g.получитьЧислоПосещений());
}
В этом тесте объект класса ПосещениеКафе не создается. Очевидно, что создавать объект и добавлять его в список должен метод добавитьПосещениеКафе объекта Лакомка.
Листинг 16.8. Лакомка.jауа
public void добавитьПосещениеКафе((double булочки, double стоимость, double вес)
{
ПосещениеКафе v =
new ПосещениеКафе(new Date(), булочки, стоимость, вес);
егоПосещения.add(v);
// добавление эл-та v в контейнер посещений
}
Опять прогоняются все тесты. Анализ программного кода в функциях тестДобавитьПосещение и тестСоздатьПосещениеКафе показывает, что он частично дублируется. Обе функции создают одинаковые локальные переменные и инициализируют их одинаковыми значениями. Чтобы избавиться от дублирования, проведем рефакторинг тестируемой программы и сделаем локальные переменные свойствами класса.
Листинг 16.9. ТестЛакомки.jауа
import junit.framework.*;
import ПосещениеКафе;
import java.util.Date;
public class ТестЛакомки extends TestCase
{
private double булочки - 7.0; // 7 булочек
private double стоимость = 12.5 * 7;
// цена 1 булочки = 12.5 p.
private double вес = 60.0; // взвешивание лакомки
private double дельта = 0.0001; // точность
public ТестЛакомки(String name)
{
super(name);
}
public void тестСоздатьПосещениеКафе()
{
Date дата = new Date();
ПосещениеКафе v = new ПосещениеКафе(дата. булочки.
стоимость, вес);
assertEquals(date, v.получитьДату());
assertEquals(12.5 * 7. v.получитьСтоимость(). дельта);
assertEquals(7.0. v.получитьБулочки(). дельта);
assertEquals(60.0. v.получитьВес(), дельта);
assertEquals(12.5. v.получитьЦену(). дельта):
}
public void тестСоздатьЛакомку()
{
Лакомка g = new Лакомка ();
assertEquals(0. g.получитьЧислоПосещений());
}
public void тестДобааитьПосещение()
{
Лакомка g = new Лакомка();
g.добавитьПосещениеКафе(булочки. стоимость, вес);
assertEquals(1. g.получитьЧислоПосещениРК));
}
}
Еще раз подчеркнем: наличие тестов позволяет определить, что этот рефакторинг ничего не разрушил в программе. Мы будем убеждаться в этом преимуществе постоянно, после очередного применения рефакторинга для реструктуризации программы. Каждый раз после внесения в код изменений запускаются тесты и проверяется работоспособность программы.
Очередная задача — после добавления к Лакомке объектов ПосещениеКафе у Лакомки можно запрашивать генерацию отчетов. Сначала напишем тесты, начнем с простейшего теста.
Листинг 16.10. TecтЛакомки.java
public void тестОтчетаОдногоПосещения()
{
Лакоика g = new Лакоика();
g.добавитьПосещениеКафе(булочки. стоимость, вес);
Отчет r = g.создатьОтчет();
assertEquals(0. r.получитьИзменениеВеса(), дельта);
assertEqualз(булочки, г.получитьПотреблениеБулочек(),
дельта);
assertEquals(0, r.получитьВесНаБулочку(), дельта);
assertEquals(стоимость. r.получитьСтоимостьБулочек(),
дельта);
}
При создании этого тестового варианта мы обдумали детали генерации отчета. Во-первых, Лакомка должна обладать методом создатьОтчет. Во-вторых, этот метод должен возвращать объект класса с именем Отчет. В-третьих, Отчет должен иметь несколько методов-селекторов.
Значения, возвращаемые методами-селекторами, следует проанализировать. Для вычисления изменения веса (или приращения веса на одну булочку) одного посещения кафе недостаточно. Чтобы вычислить эти значения, необходимы, как минимум, два посещения, С другой стороны, одного визита достаточно, чтобы сосчитать потребление и стоимость булочек.
Разумеется, тестовый вариант не компилируется. Поэтому необходимо добавить соответствующие методы и классы. Сначала добавим код, обеспечивающий компиляцию, но не обеспечивающий выполнение тестов.
Листинг 16.11. Лакомка.java, TecтЛакомки.java и Отчет.jаvа
Лакомка.java
public Отчет создатьОтчет()
{
return new Отчет();
}
ТестЛакомки.java
public void тестОтчетаОдногоПосещения()
{
Лакомка g = new Лакомка();
g.добавитьПосещениеКафе(булочки, стоимость, вес);
Отчет r = g.создатьОтчет();
assertEquals(0, r.получитьИзменениеВеса(), дельта);
assertEquals(булочки. r.получитьПотреблениеБулочек(),
дельта);
assertEquals(0. r.получитьВесНаБулочку(), дельта);
assertEquals(cтоимость,. r.получитьСтоимостьБулочек(),.
дельта);
}
Отчет.java
public class Отчет
{
public double получитьИзменениеВеса()
{return егоИзменениеВеса;}
public double получитьВесНаБулочку()
{return егоВесНаБулочку;}
public double получитьСтоииостьБулочек()
{return егоСтоимостьБулочек;}
public double получитьЛотреблениеБулочек()
{return егоПотреблениеБулочек;}
private double егоИзменениеВеса;
private double егоВесНаБулочку;
private double егоСтоимостьБулочек;
private double егоПотреблениеБулочек;
}
Код в листинге 16.11 компилируется и запускается, но его недостаточно для того, чтобы прошли тесты. Нужен рефакторинг кода. Для начала сделаем минимально возможные изменения.
Листинг 16.12. Лакомка.java и Отчет.java
Лакомка.java
public Отчет создатьОтчет()
{
Отчет r = new Отчет();
ПосещениеКафе v = (ПосещениеКафе) егоПосещения. Get(0);
// занести в v первый элемент из контейнера посещений
r.устВесНаБулочку(0);
r.устИзменениеВеса(0);
r.устСтоимостьБулочек(v.получитьСтоимость());
r.устПотреблениеБулочек(v.получитьБулочки()):
return r;
}
Отчет.java
public void устВесНаБулочку (double wpb)
{егоВесНаБулочку = wpb;}
public void устИзменениеВес(double kg)
{егоИзменениеВеса = kg;}
public void устСтоимостьБулочек(double ct)
(егоСтоимостьБулочек = ct;}
public void устПотреблениеБулочек (double b)
{егоПотреблениеБулочек = b;}
Предполагаем, что Лакомке разрешено только одно посещение. В этой версии метода создатьОтчет устанавливаются и возвращаются значения свойств Отчета.
Такой способ разработки метода создатьОтчет может показаться странным, ведь его реализация не завершена. Однако преимущество по-прежнему в том, что между каждой компиляцией и тестированием вносятся только контролируемые добавления. Если что-то отказывает, можно просто вернуться к предыдущей версии и начать сначала, необходимость в сложной отладке отсутствует.
Для завершения кода продумаем тесты для Лакомки без посещений и с несколькими посещениями кафе. Начнем с теста и кода для варианта без посещений.
Листинг 16.13. TecтЛакомки.java и Лакомка.jауа
ТестЛакомки.java
public void тестОтчетаБезПосещений()
{
Лакомка g = new Лакомка();
Отчет r= g.создатьОтчет();
assertEquals(0, r.получитьИзменениеВеса(). дельта);
assertEquals(0, r.получитьПотреблениеБулочек(), дельта);
assertEquals(0, r.получитьВесНаБулочку()), дельта;
assertEquals(0, r.получитьСтоимостьБулочек(), дельта);
}
Лакомка.Java
public Отчет создатьОтчет()
{
Отчет r = new Отчет();
if (егоПосещения.size() = 0)
{
r.устВесНаБулочку(0);
r.устИзиенениеВеса(0);
r.устСтоимостьБулочек(0);
r.устПотреблениеБулочек(0);
}
else
{
ПосещениеКафе v = (ПосещениеКафе) егоПосещения.get(0);
// занести в v первый элемент из контейнера посещений
r.устВесНаБулочку(0);
r.устИзменениеВеса(0);
r.устСтоимостьБулочек(v.получитьСтоимость());
r. устПотреблениеБулочек (v.получитьБулочки ()):
}
return r;
}
Теперь начнем создавать тестовый вариант для нескольких посещений.
Листинг 16.14. ТестЛакомки.jауа
public void тестОтчетаНесколькихПосещений()
{
Лакомка g = new Лакомка();
g.добавить(ПосещениеКафе(7. 87.5, 60.7);
g.добавитьПосещениеКафе(14. 175, 62.1);
g.добавитьПосещениеКафе(28, 350. 64.9);
Отчет r= g.создатьОтчет();
assertEquals(4.2, r.получитьИзменениеВеса(), дельта);
assertEquals(49. r.получитьПотреблениеБулочек(), дельта);
assertEquals(0.086, r.получитьВесНаБулочку(), дельта);
assertEquals(612.5, r.получитьСтоииостьБулочек(), дельта);
}
Мы установили число посещений для Лакомки равным трем. Предполагается, что цена булочки составляет 12,5 руб., а изменение веса — 0,1 кг на одну булочку. Таким образом, за 175 руб. лакомка покупает и съедает 14 булочек, полнея на 1,4 кг.
Но здесь какая-то ошибка. Скорость изменения веса должна определяться коэффициентом 0,1 кг на одну булочку. А если разделить 4,2 (изменение веса) на 49 (количество булочек), то получаем коэффициент 0,086. В чем причина несоответствия?
После размышлений становится понятно, что вес лакомки регистрируется на выходе из кафе. Поэтому приращение веса и потребление булочек во время первого посещения не учитывается. Изменим исходные данные теста.
Листинг 16.15. ТестЛакомки.jауа
public void тестОтчетаНесколькихПосещений()
{
Лакомка g = new Лакомка();
g.добавитьПосещениеКафе(7. 87.5. 60.7);
g.добавитьПосещениеКафе(14. 175. 62.1);
g.добавитьПосещениеКафе(28. 350. 64.9);
Отчет r - g.создатьОтчет();
assertEquals(4.2, r.получитьИзменениеВеса(), дельта);
assertEquals(42, r.получитьПотреблениеБулочек(), дельта);
assertEquals(0.1, r.получитьВесНаБулочку(), дельта);
assertEquals(612.5, r.получитьСтоимостьБулочек(), дельта);
}
Этот тест корректен. Никогда не известно, с чем встретишься при написании тестов. Можно быть уверенным лишь в том, что, определяя понятия дважды (при написании тестов и кода), вы найдете больше ошибок, чем при простом написании кода.
Теперь добавим код, обеспечивающий прохождение теста из листинга 16.15.
Листинг 16.16. Лакомка.java
public Отчет создатьОтчет()
{
Отчет r = new Отчет ();
if (егоПосещения.size() = 0)
{
r.устВесНаБулочку(0);
r.устИзменениеВеса(0);
r.устСтоимостьБулочек(0);
r.устПотреблениеБулочек(0);
}
else if (егоПосещения.size() = 1)
{
ПосещениеКафе v = (ПосещениеКафе) егоПосещения.get(0);
// занести в v первый элемент из контейнера посещений
r.устВесНаБулочку(0);
r.устИзменениеВеса(0);
r.устСтоимостьБулочек(v.получитьСтоимость());
r.устПотреблениеБулочек(v.получитьБулочки());
}
else
{
double первыйЗамер = 0;
double последнийЗамер = 0;
double общаяСтоиность = 0;
double потреблениеБулочек = 0;
for (int i = 0; i < егоПосещения.size(); i++)
// проход по всем элементам контейнера посещений
{
ПосещениеКафе v = (ПосещениеКафе)
егоПосещения.get(i);
// занести в v 1-й элемент из контейнера посещений
if (i = = 0)
{
первыйЗамер = v.получитьВес();
// занести в первыйЗамер вес при 1-м посещении
потреблениеБулочек -= v.получитьБулочки();
}
if (i= = егоПосещения.size()- 1) последнийЗамер =