параметров за последние 24 часа.
Система должна обеспечивать постоянный вывод на дисплей текущих значений всех восьми первичных и производных параметров, а также текущее время и дату. Пользователь должен иметь возможность увидеть максимальные и минимальные значения любого из первичных параметров за 24 часа, сопровождаемые информацией о времени произведения соответствующего замера.
Система должна позволять пользователю проводить калибровку датчиков по известным опорным значениям, а также устанавливать текущие время и дату.
8.1. Анализ
Определение границ рассматриваемой задачи
Врезка ознакомила вас с требованиями к системе мониторинга погоды. Это довольно простая задача, решение которой позволяет обойтись всего несколькими классами. Инженер, не вполне искушенный во всех особенностях объектно-ориентированного анализа, может сделать поспешный вывод о том, что в данном случае наиболее простым и эффективным будет отказ от объектно-ориентированного подхода. Он обратится к рассмотрению потоков данных и входных/выходных значений. Тем не менее, как мы увидим в дальнейшем, даже для такой небольшой системы лучше ввести объектно-ориентированную архитектуру, которая прекрасно проиллюстрирует некоторые основные принципы, лежащие в основе объектно- ориентированной разработки.
Мы начнем наш анализ с рассмотрения аппаратной части системы. Это задача системного анализа. Она включает в себя такие вопросы, как технологичность и стоимость системы, которые выходят за рамки данной книги. Для того, чтобы сузить проблему, ограничившись анализом и проектированием только программных средств, сделаем следующие стратегические предположения об аппаратной части:
• Используется компьютер с одним процессором i486 [Это решение может показаться избыточным, но сейчас платформы на базе 486-го процессора ненамного дороже компьютеров с процессорами предыдущих поколений. Закладывая в требования избыточную производительность компьютера, мы обеспечиваем больший срок жизни разрабатываемой системы].
• Системные время и дата поддерживаются встроенными часами, соответствующие значения отображаются в оперативную память.
• Температура, барометрическое давление и влажность определяются встроенными контроллерами, которые соединены с соответствующими датчиками; показания контроллеров также отображены в оперативную память.
• Направление ветра измеряется с помощью флюгера с точностью до одного из 16 направлений; скорость ветра определяется анемометром со счетчиком оборотов.
• Ввод команд пользователем осуществляется с помощью клавишной панели (похожей на телефонную), сигналы которой обрабатываются с помощью встроенной платы. Эта же плата обеспечивает звуковой сигнал после каждого нажатия клавиши. Последняя команда пользователя сохраняется в памяти.
• Экраном служит обычный жидкокристаллический дисплей. Встроенный контроллер дисплея обеспечивает вывод на экран небольшого набора графических примитивов: линий и дуг, закрашенных областей и текста.
• Встроенный таймер посылает прерывания через каждую 1/60 долю секунды.
На рис. 8-1 приведена диаграмма, иллюстрирующая состав аппаратной части системы.
Мы решили несколько упростить аппаратную модель для того, чтобы уделить больше внимания программной части. Очевидно, можно было бы вообще не определять типы устройств ввода команд и вывода информации на экран, что почти не затронуло бы архитектуру системы. Одной из особенностей объектно-ориентированного подхода является стремление говорить на языке проблемной области, что облегчает проведение параллелей между программными абстракциями и ключевыми понятиями исходной задачи. Изменения в аппаратной части оказывают влияние лишь на некоторые нижние уровни системы.
Что касается особенностей организации ввода/вывода посредством отображения в память, то нам не хотелось бы подробно на них останавливаться, так как эти детали в большой степени зависят от способа реализации проекта. Мы можем легко изолировать наши программные абстракции от этих 'неинтересных' подробностей, скрыв их в реализациях соответствующих классов. Например, имеет смысл создать простой класс для определения текущего времени и даты: для этого надо провести небольшой анализ, в процессе которого придется рассмотреть роли и обязанности данной абстракции [На самом деле, прежде чем создавать класс, полезно порыться в доступных вам библиотеках и постараться найти там что-нибудь похожее. Класс времени и даты - хороший кандидат на повторное использование, и скорее всего кто-нибудь уже разработал и отладил подобную абстракцию. Однако, для целей нашего изложения лучше предположить, что такого класса в готовом виде не нашлось]. Мы, в частности, могли бы прийти к решению, что данный класс ответственен за отслеживание информации о текущем времени в часах, минутах и секундах, а также о дате (текущий месяц, день и год). В результате анализа мы также могли бы сделать вывод о том, что среди обязанностей класса необходимо выделить две услуги: предоставление информации о текущем времени (currentTime) и о текущей дате (currentDate). Операция currentTime возвращает текстовую строку следующего формата:
13:56:42
показывающую текущие час, минуту и секунду. Операция currentDate возвращает строку следующего формата:
3-20-98
показывающую текущие месяц, день и год.
Дальнейший анализ подсказывает, что могут понадобиться более полные абстракции, позволяющие клиенту выбирать между 12- и 24-часовым форматом времени. Одно из возможных решений - введение дополнительного модификатора setFormat, меняющего формат представления текущего времени.
Определив поведение данной абстракции с точки зрения клиента, мы предлагаем затем четко разделить интерфейс класса и его реализацию. Основная идея состоит в том, чтобы сначала определить внешний интерфейс каждого класса, не задумываясь при этом об особенностях его внутреннего строения. Реализация интерфейса класса через его внутреннее устройство происходит на этапе разработки. Реализация класса осуществляет связь между внешним представлением об абстракции и ее воплощением в конкретной аппаратной платформе, которую, как правило, инженер-программист изменить не в силах. Но при этом, конечно, надо следить, чтобы разрыв между программной абстракцией и внутренним устройством не был слишком большим и не требовал от программиста громадных усилий по 'склеиванию' совершенно разнородных понятий.
Предположим, что наша аппаратная модель обеспечивает доступ ко времени и дате как к 16-битовому целому числу, которое показывает, сколько секунд прошло со времени включения компьютера [В простейшем случае может использоваться аппаратно реализованный счетчик, увеличивающий свое значение на единицу каждую секунду. Более изощренная реализация может использовать микросхему времени/даты с питанием от батарейки. В любом случае, внешний вид этого класса (контракт с клиентами) должен быть одним и тем же. Наша реализация класса отвечает за поддержание этого контракта на данной аппаратной платформе]. Тогда наш класс времени и даты должен обеспечивать пересчет этой сырой информации в полезные значения, что, в свою очередь, диктует необходимость добавления новых операций setHour, setSecond, setDay, setMonth и setYear, определяющих на основе первичных данных текущие час, секунду, день, месяц и год.
Теперь подведем итоги. Абстракция класса времени и даты выглядит так:
Имя: