Файл: Разработка модуля для выполнения операций с натуральными числами в шестнадцатеричной системе счисления.pdf
Добавлен: 17.06.2023
Просмотров: 59
Скачиваний: 4
1.3. Достоинства и недостатки модульного программирования
Модульное программирование — это организация программы как совокупности небольших независимых блоков, называемых модулями, структура и поведение которых подчиняются определенным правилам. Использование модульного программирования позволяет упростить тестирование программы и обнаружение ошибок. Аппаратно-зависимые подзадачи могут быть строго отделены от других подзадач, что улучшает мобильность создаваемых программ[3].
Модуль — функционально законченный фрагмент программы. Во многих языках (но далеко не обязательно) оформляется в виде отдельного файла с исходным кодом или поименованной непрерывной её части. Некоторые языки предусматривают объединение модулей в пакеты.
В основе модульного программирования лежат три концепции. Первая — это принцип утаивания информации Парнаса. Всякий компонент утаивает единственное проектное решение, то есть модуль служит для утаивания информации. Подход к разработке программ заключается в том, что сначала формируется список проектных решений, которые особенно трудно принять или которые, скорее всего, будут меняться. Затем определяются отдельные модули, каждый из которых реализует одно из указанных решений.
Второй концепцией является аксиома модульности Коуэна. Модуль — независимая программная единица, служащая для выполнения некоторой определённой функции программы и для связи с остальной частью программы. Программная единица должна удовлетворять следующим условиям:
- блочность организации, то есть возможность вызвать программную единицу из блоков любого уровня вложенности;
- синтаксическая обособленность, то есть выделение модуля в коде синтаксическими элементами;
- семантическая независимость, то есть независимость от места, где программная единица вызвана;
- общность данных, то есть наличие собственных данных, сохраняющихся при каждом обращении;
- полнота определения, то есть самостоятельность программной единицы.
Третьей концепцией модульного программирования является сборочное программирование Цейтина, согласно которой модули — это программные кирпичи, из которых строится программа[3].
Существуют три основные предпосылки к модульному программированию:
- Стремление к выделению независимой единицы программного знания. В идеальном случае всякая идея (алгоритм) должна быть оформлена в виде модуля.
- Потребность организационного разделения крупных разработок.
- Возможность параллельного исполнения модулей
(в контексте параллельного программирования).
Функциональная спецификация модуля должна включать:
- синтаксическую спецификацию его входов, которая должна позволять построить на используемом языке программирования синтаксически правильное обращение к нему;
- описание семантики функций, выполняемых модулем по каждому из его входов.
Существуют три основные разновидности модулей:
- Функциональные модули, реализующие, как правило, одну какую-либо определенную функцию. Основным и простейшим модулем практически во всех языках программирования является процедура или функция.
- Информационные модули, реализующие, как правило, несколько операций или функций над одной и той же структурой данных (информационным объектом), которая считается неизвестной вне этого модуля.
- Логические модули, объединяющие набор функциональных и/или информационных модулей.
Как можно заметить, модульное программирование призвано упростить разработку приложений, взамен скрывая от разработчика истинные процессы, происходящие в модуле. Сам модуль выступает в роли "черного ящика" — при его вызове мы знаем только какие данные ему необходимо передать и чем будет являться ответ.
Модуль может являться как функцией (процедурой) в функциональном (процедурном) языке программирования, как классом в объектно-ориентированном языке программирования, так и отдельной независимой программой, работающей под управлением программной оболочки[3].
Именно представление в виде отдельной программы будет взято за основу разработки модуля для выполнения операций с натуральными числами в шестнадцатеричной системе счисления.
При очевидном недостатке подобного подхода, а именно зависимости программной оболочки от программного модуля, можно найти и ряд преимуществ. Во-первых, упрощается разработка приложения, так как всё, что будет необходимо для выполнения операций с натуральными числами в шестнадцатеричной системе счисления — это передать определённый запрос модулю и получить готовый ответ. Так же, достоинством подхода является исполнение модуля в отдельном потоке, что благотворно влияет на производительность.
2. Разработка алгоритма программного модуля
2.1. Задачи, решаемые программным модулем
Разрабатываемый программный модуль является отдельной программой и предназначен для работы под управлением программной оболочки и выполнением арифметических операций над шестнадцатеричными числами. Исходя из этого, программный модуль имеет следующие задачи:
- Быть доступным для подключения к программной оболочке.
- Принимать строковые запросы от оболочки.
- Производить вычисления, в соответствии с принятым запросом.
- Отправлять оболочке результаты вычислений.
Программный модуль может использоваться в различных оболочках, например, в шестнадцатеричном калькуляторе. На одной вычислительной машине или в вычислительной сети может работать несколько программных оболочек, требующих наличия разрабатываемого программного модуля.
В таком случае есть несколько путей обеспечения программных оболочек модулем. Если программный модуль поддерживает связь только с одной программной оболочкой, то количество программных модулей должно быть не ниже количества запущенных программных оболочек. Такой подход допустим, если заведомо известно, что программная оболочка на машине или в сети будет запускаться только одна. В случае превышения количеством оболочек количества модулей возникнет программный конфликт — все существующие модули заняты, дальнейший запуск программ невозможен.
Если заранее неизвестно, сколько программных оболочек будет одновременно работать на вычислительной машине или в вычислительной сети, то лучшим подходом является наделение модуля средствами обслуживания нескольких оболочек одновременно. Это может быть достигнуто, например, разделением подключений к оболочкам на потоки. При таком подходе необходимо добиться того, чтобы программный модуль не требовал излишнего количества вычислительных ресурсов для работы[3].
Однако лучший способ предоставления модулю средств одновременного обслуживания нескольких программных оболочек — принудительное завершение работы модуля после каждого обращения извне. Таким подходом, например, пользуется командная строка операционных систем семейства Windows. Преимущества данного подхода очевидны — всего лишь один программный модуль сможет работать с практически неограниченным количеством программных оболочек и при этом он не будет громоздким и будет нетребователен к вычислительным ресурсам.
Существует так же и один недостаток данного подхода — если оболочка обратится к модулю в то время, когда он будет обрабатывать запрос от другой оболочки, то оболочка получит ошибку — модуль занят. В связи с этим, любая программная оболочка, нуждающаяся в использовании модуля для выполнения операций с натуральными числами в шестнадцатеричной системе счисления, должна быть построена таким образом, что, получив ошибку "модуль занят", будет посылать свой запрос снова и снова, пока не получит ответ или пока не истечет время ожидания ответа.
2.2. Функциональная составляющая модуля и принцип его работы
Модуль для выполнения операций с натуральными числами в шестнадцатеричной системе счисления будет построен в виде консольного приложения, а общение с программной оболочкой — организовано через стандартные потоки ввода и вывода.
Стандартные потоки ввода-вывода — потоки процесса, имеющие номер (дескриптор), зарезервированный для выполнения некоторых «стандартных» функций. Как правило (хотя и не обязательно), эти дескрипторы открыты уже в момент запуска задачи (исполняемого файла)[4].
Поток номер 0 зарезервирован для чтения команд пользователя или входных данных. При интерактивном запуске программы по умолчанию нацелен на чтение с устройства текстового интерфейса пользователя (клавиатуры).
Поток номер 1 зарезервирован для вывода данных, как правило
(хотя и не обязательно) текстовых. При интерактивном запуске программы по умолчанию нацелен на запись на устройство отображения (монитор)[4].
Таким образом, при отладке созданного модуля возможно будет пользоваться командной строкой Windows, посылая запросы и получая ответы через терминал. При работе модуля под управлением программной оболочки стандартные потоки ввода и вывода будут перенаправлены в процесс программной оболочки.
Запросы, допустимые к обработке модулем, должны быть строго стандартизированы. Именно поэтому возникает необходимость в разработке протокола запросов и ответов.
Протокол передачи данных — стандарт, описывающий правила взаимодействия функциональных блоков при передаче данных[3].
Согласно задачам, решаемым программным модулем, модуль должен принимать строковые запросы, ведь шестнадцатеричное представление натурального числа — ни что иное, как строка.
Разрабатываемый модуль выполняет бинарные операции (унарные операции, например инкрементация или декрементация, так же сводятся к бинарным), а значит, строка запроса должна содержать два шестнадцатеричных числа и идентификатор операции.
Идентификаторы операций могут быть представлены любыми символами, но интуитивно понятные обозначения арифметических операций — это символы "+", "-", "*" и "/". Кроме того, ни один из этих символов не является шестнадцатеричной цифрой.
Единственное требование к передачи двух шестнадцатеричных чисел — что бы они не стояли слитно одно за другим, ведь в таком случае будет не ясно, где конец первого числа, а где начало второго.
Разумным решением будет оформить запрос в следующем виде:
[первое число][идентификатор операции][второе число]
Не являясь шестнадцатеричной цифрой, идентификатор операции отлично подходит на роль разделителя двух чисел. Кроме того, такая форма записи интуитивно понятна человеку, ведь фактически представляет собой два числа и знак между ними.
Ответом программного модуля на такой запрос будет строка, представляющая собой шестнадцатеричное число, получившееся в результате выполнения заданной операции над заданными числами.
2.3. Алгоритм программного модуля
На основе задач, решаемых программным модулем и возлагаемых на него функций был составлен алгоритм, блок-схема которого
отображена в приложении 1. Блок-схема выполнена на основании
ГОСТ 19.701-90 "Схемы алгоритмов, программ, данных и систем. Условные обозначения и правила выполнения"[3].
Рассматривая данную схему, следует отметить, что первым пунктом идёт подключение модуля к программной оболочке. Подключение происходит по запросу программной оболочки.
Далее, модуль для выполнения операций с натуральными числами в шестнадцатеричной системе счисления ожидает от программной оболочки передачи данных по разработанному протоколу. Программная оболочка передаёт модулю строку, содержащую информацию как о значении двух шестнадцатеричных числах, над которыми будет производиться операция, так и о том, какая операция была выбрана. Для приёма данных используется стандартный поток ввода, известный также, как поток номер 0[4].
Следующим шагом является расшифровка переданной строки, в частности, выбор операции. Программный модуль сравнивает код символа, переданного в качестве идентификатора операции с четырьмя эталонами, расположенными в его сегменте данных, и по результатам сравнения переходит в ту часть инструкций, которая соответствует выбранной операции.
Теперь модуль выполняет операцию с натуральными числами, переданными ему программной оболочкой. Всего предусмотрено четыре бинарные арифметические операции — сложение, вычитание, умножение и деление.
Получившийся результат немедленно передаётся программной оболочке в виде строки, содержащей шестнадцатеричное число. Для передачи данных используется поток номер 1, являющийся стандартным потоком вывода[4].
Отключение от программной оболочки, в отличие от подключения, выполняется не по запросу оболочки, а по инициативе модуля, сразу после передачи результата вычислений. Обосновано это тем, что заданная одной программной оболочкой операция уже выполнена, и возможно, другая программа ожидает, когда модуль освободится для новой задачи.