всех сообщений (например, ТЕМПЕРАТУРА, или скорость) находятся, в этом случае, в ведении единственного класса; они не разбросаны по множеству различных абстракций.
Рассмотрение задачи локализации ставит перед разработчиком ряд дополнительных вопросов, не выраженных явным образом в требованиях к системе. Как следует показывать температуру, по Цельсию или по Фаренгейту? В чем отображать скорость ветра, в километрах в час или в милях в час? Ясно, что наше программное обеспечение не должно нас жестко ограничивать. Для обеспечения гибкости в использовании системы конечным пользователем необходимо добавить к описаниям классов TemperatureSensor и WindSpeedSensor еще одну операцию, setMode, устанавливающую нужную систему измерений. Также следует добавить в описание этих классов новую обязанность, предусматривающую возможность установки вновь создаваемых объектов в известное состояние. И, наконец, мы должны изменить описание операции DisplayManager::drawStaticItems таким образом, чтобы при изменении единиц измерений соответствующим образом менялась панель дисплея.
В результате нам придется добавить к списку режимов работы системы еще один сценарий:
• Установка единиц измерения температуры и скорости ветра.
Мы отложим рассмотрение данного режима до того, как изучим другие сценарии. Мониторинг вторичных параметров, в частности трендов температуры и давления, можно обеспечить на основе протоколов уже приведенных ранее классов TemperatureSensor и PressureSensor. Однако, чтобы полностью определить сценарий мониторинга, придется добавить еще два класса (назовем их WindChill и DewPoint), предназначенных для определения коэффициента жесткости погоды и точки образования росы. Эти абстракции не отождествляются с датчиками и вообще с чем-либо осязаемым. Их задача - вычисление значений параметров. Они выступают в роли агентов, сотрудничающих с другими классами. Именно класс WindChill использует для вычислений информацию, содержащуюся в TemperatureSensor и WindSpeedSensor, а класс DewPoint сотрудничает с классами TemperatureSensor и HimiditySensor. Классы Windchill и DewPoint сотрудничают и с классом Sampler, так как они используют аналогичный механизм опроса датчиков. Рис. 8-9 иллюстрирует набор классов и связи между ними, необходимые для реализации рассмотренного сценария. Он почти не отличается от диаграммы классов, приведенной ранее на рис. 8-8.
Почему мы решили определить WindChill и DewPoint в качестве классов, вместо того, чтобы реализовать вычисления соответствующих параметров с помощью отдельных функций? Потому что каждый из них удовлетворяет условиям, позволяющим выделить их в отдельные абстракции. Экземпляры этих классов обладают характерным поведением (вычисление определенных величин по определенному алгоритму), имеют в каждый момент времени определенное состояние (зависящее от состояния связанных с ними датчиков) и уникальны (любая ассоциация между экземплярами датчиков скорости ветра и температуры требует собственного экземпляра WindChill). 'Объективация' этих алгоритмических абстракций повышает вероятность их повторного использования в архитектурах систем: классы WindChill и DewPoint легко можно будет перенести из нашего приложения в другие программные системы, потому что каждый из них обладает понятным внешним интерфейсом и четко выделяется как отдельная абстракция.
Далее рассмотрим различные сценарии взаимодействия пользователя и системы. Предоставление пользователю оптимальной последовательности действий для выполнения его задач является так же, как и проектирование графического интерфейса, в большой степени искусством. Изучение этого вопроса выходит за рамки данной книги, но основную мысль можно вкратце выразить следующим образом: используйте прототипирование, оно существенно уменьшает риск при разработке интерфейса пользователя. Кроме того, если архитектура системы является объектно-ориентированной, то снижаются затраты, связанные с изменением организации интерфейса пользователя.
Рассмотрим некоторые из возможных сценариев взаимодействия пользователя с системой:
Вывод на экран максимальных и минимальных значений выбранного параметра.
1. Пользователь нажимает клавишу SELECT. 2. Система выводит на экран сообщение SELECTING. 3. Пользователь нажимает одну из следующих клавиш: WIND SPEED, TEMPERATURE, PRESSURE или HUMIDITY; нажатие всех остальных клавиш (кроме клавиши RUN) игнорируется. 4. Название выбранного параметра начинает мигать на экране. 5. Пользователь нажимает одну из клавиш UP или DOWN, выбирая тем самым, какое значение - максимальное или минимальное - будет выведено на экран; нажатие всех остальных клавиш (кроме клавиши Run) игнорируется. 6. Система выводит на экран выбранное значение, а также время его замера. 7. Переход управления к пункту 3 или 5.
Замечание: для прекращения работы в данном режиме пользователь нажимает клавишу RUN, при этом экран дисплея возвращается в первоначальное состояние.
После рассмотрения этого сценария мы приходим к выводу о необходимости расширить описание класса DisplayManager, добавив к нему операции flashLabel (переключает вывод названия параметра в режим мигания и обратно, в зависимости от аргумента) и displayMode (выводит на дисплей текстовое сообщение).
Установка времени и даты подчиняется аналогичному сценарию:
Установка времени и даты.
1. Пользователь нажимает клавишу SELECT. 2. Система выводит на экран сообщение SELECTING. 3. Пользователь нажимает одну из следующих клавиш: TIME или DATE; нажатия всех остальных клавиш (кроме клавиши RUN и клавиш, перечисленных в пункте 3 предыдущего сценария) игнорируются. 4. Название выбранного параметра, а также первое поле его значения (для времени - это час, для даты - месяц) начинают мигать на экране. 5. Пользователь нажимает одну из клавиш LEFT или RIGHT для перехода на другое поле; пользователь нажимает одну из клавиш UР или DOWN для увеличения или уменьшения значения выделенной величины. 6. Переход управления к пункту 3 или 5.
Замечание: для прекращения работы в данном режиме пользователь нажимает клавишу RON, при этом экран дисплея возвращается в первоначальное состояние, и происходит переустановка времени и даты.
Сценарий калибровки датчика следует той же схеме:
Калибровка датчика.
1. Пользователь нажимает клавишу CALIBRATE. 2. Система выводит на экран сообщение CALIBRATING. 3. Пользователь нажимает одну из следующих клавиш: WIND SPEED, TEMPERATURE, PRESSURE или HUMIDITY; нажатия всех остальных клавиш (кроме клавиши RUM) игнорируются. 4. Название выбранного параметра начинает мигать на экране. 5. Пользователь нажимает одну из клавиш Up или DOWN, задавая тем самым, какое калибровочное значение, максимальное или минимальное, будет переопределяться. 6. Соответствующее калибровочное значение начинает мигать на экране. 7. Пользователь нажимает клавиши ПР или DOWN для изменения значения выделенной величины. 8. Переход управления к пункту 3 или 5.
Замечание: для прекращения работы в данном режиме пользователь нажимает клавишу RUN, при этом экран дисплея возвращается в первоначальное состояние, и происходит перерасчет калибровочной функции.
На время калибровки все экземпляры класса Sampler должны прекратить считывание параметров, в противном случае будут показаны ошибочные данные. Таким образом, мы должны добавить в описание класса sampler еще две операции: