понедельник, 22 августа 2011 г.

С чего начать проектирование программы? Часть 2


Прежде чем следовать дальше, повторим основные тезисы предыдущей статьи:

  1. Любая программа разрабатывается для решения конкретных задач. Эти задачи будем называть полезными функциями или просто функциями.
  2. Прежде чем проектировать архитектуру программы, нужно сначала разработать алгоритм выполнения каждой полезной функции. Такой алгоритм в машиностроении называется технологией или технологическим процессом.
  3. Технологический процесс представляет собой последовательность операций, которая может быть описана либо в текстовом виде в форме варианта использования, либо в форме блок-схемы, flowchart.
  4. Порядок проектирования можно описать в виде такой схемы:

Функция --> Технологический процесс --> Архитектура

Сначала нужно сформулировать полезные функции, которые будет выполнять проектируемая программа. Затем – описать технологический процесс для каждой из них. И только после этого можно проектировать архитектуру.


Этап проектирования программы, на котором разрабатывается технологический процесс, будем называть технологическим проектированием. Данная серия статей посвящена этому этапу.

Вернёмся к задаче про датчики и метеостанцию. В предыдущей статье мы остановились на такой схеме технологического процесса:


От датчика температуры и датчика атмосферного давления, подключённых к компьютеру через концентратор, данные поступают на порт. Программа считывает сообщение с порта через определённый протокол, определяет, от какого датчика оно пришло, и отправляет на обработку соответствующему модулю. Далее сообщение парсится, и из него извлекается значение температуры или атмосферного давления. Затем оно преобразуется к заданным единицам измерения и передаётся контролу, ответственному за вывод значения на экран.

Данная технологическая цепочка выглядит логично. Однако если мы попытаемся реализовать её в цикле (ведь данные с порта нужно считывать регулярно, а не один раз), то столкнёмся с проблемой:

Как часто опрашивать порт?

С одной стороны, датчики поставляют нам данные с определённой частотой (например, 10 раз в секунду).

С другой стороны, может оказаться, что нет необходимости обновлять на экране значение температуры и атмосферного давления с точно такой же частотой.

Более того, значения метеорологических параметров могут выводиться на экране только по запросу пользователя или поступать на разные терминалы одновременно.

Чтобы отвязать частоту опроса датчиков от частоты обновления информации на экране, разобъём единый технологический процесс на две части:


Первая часть будет отвечать за опрос датчиков и сохранение полученной информации в базе данных:


Вторая часть будет отвечать за чтение информации из базы и вывод её на экран:


Если мы посмотрим на новые схемы, то заметим, что этап преобразования параметров из одних величин в другие присутствует сразу на обеих схемах. Это сделано не случайно, т.к. конвертация значений может понадобиться как при записи в базу данных (например, когда база хранит температуру в Цельсиях, а датчик измеряет её в Фаренгейтах), так и при выводе информации на экран (например, когда пользователю нужны данные в Фаренгейтах, а в базе они хранятся в виде Цельсиев).

Каждый из вновь образованных тех. процессов мы можем выполнять с необходимой частотой. Например, данные с датчиков могут считываться по сигналу от таймера:


Информация на экране может обновляться по команде пользователя или по сигналу от другого таймера, который будет запрашивать её с другой, гораздо меньшей, частотой:


Кроме того, каждую из вновь образованных технологических цепочек можно продублировать разное количество раз. Например, опрос датчиков можно продублировать по числу опрашиваемых портов и/или по числу ядер процессора, а вывод информации на экран можно продублировать по числу терминалов, на которые нужно выводить информацию (с учётом того, что каждый пользователь может запрашивает свою, нужную ему, информацию).

Каждую технологическую цепочку можно реализовать в виде отдельного  потока в одном приложении или даже в виде самостоятельного приложения.

Резюмируя:

  1. Слишком длинные технологические процессы, как правило, распадаются на части из-за того, что к разным участкам этих процессов предъявляются разные требования (например, по частоте выполнения).
  2. Если при проектировании технологического процесса мы столкнулись с проблемой из п. 1, то нужно разделить один процесс на части и связать их между собой при помощи некой сущности, через которую оба новых процесса будут обмениваться данными.


7 комментариев:

  1. 1) На порт поступают или не поступают данные от датчика с определённой частотой. Наш поток периодически просыпается и опрашивает порт. На схеме я показал, что по сигналу от таймера, но в коде может стоять просто sleep(). Данные с порта читаются через модуль Port, который инкапсулирует работу с ним (портом).

    2) Коммуникация с датчиком может быть односторонняя - программа просто читает то, что пришло на порт. Но может быть и двухсторонняя - запрашивает данные у датчика. Эту коммуникацию инкапсулирует сущность Protocol.

    ОтветитьУдалить
  2. 3) Данные мы конвертируем дважды, потому что датчик может поставлять данные в своих единицах измерения, а датчиков одинакового вида может быть несколько. В базе мы будем хранить данные в единицах измерения базы, а на экране - отображать в тех единицах, которые запросил пользователь.

    Понятно, что на первом этапе мы попытаемся обходиться без преобразования данных или с одним преобразованием. Но в общем случае, по мере развития проекта и замены даьчиков нужно будет два преобразования.

    ОтветитьУдалить
  3. 1) Датчики и HUB на схеме не программные, а физические сущности. В первой статье я описывал пошаговое построение схемы и оставил их для понимания, откуда данные поступают. Порт не должен хранить никаких ссылок. В него можно что-то записать или из него прочитать.

    При этом, порт на схеме - это сущность, которая инкапсулирует работу с портом, т.е. предоставляет интерфейс для записи и чтения.

    2) Схема сделана детально не случайно, а намеренно. На мой взгляд, принципиальная схема, дающая лишь общее представление, не так важная, как детальная. Подробная схема даёт нам представление об операциях, которые нужно выполнить и запрограммировать и, соответственно, приближает нас к созданию диаграммы классов.

    ОтветитьУдалить
  4. А я бы проще сделал: повесит тупо таймер на UI и считывал бы так часто как надо именно UI, а где-нить на уровне Dispatcher можно организовать кеш, чтобы даже миллион клиентов не завалил датчик.

    ОтветитьУдалить
  5. БД - не обязательно СУБД. это может быть и кольцевой буфер.

    ОтветитьУдалить
  6. В данном случае БД "разворачивает стрелочки", если сделать диаграмму "кто кого вызывает", то все стрелочки будут сходиться к БД. Это кстати и является причиной того что большой процесс распадается на маленькие.

    Кеш в свою очередь прозрачен для вызывающей стороны. и стролочки вызвово остаются в том же направлении.

    ОтветитьУдалить
  7. На мой взгляд, стрелочки разворачивает не база, а концы "цепочки" - датчик и экран. Нам нужно опрашивать датчик с одной частотой, а обновлять информацию на экране - с другой (или вообще делать это по запросу пользователя.)

    Об этом я и пишу в этой статье.

    ОтветитьУдалить