Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Клуб профессиональных программистов :: Выпуск #135


Клуб профессиональных программистов «Весельчак У»
Информационная рассылка сайта и форума.  Выпуск 135.  13 января 2012 г.

Здравствуйте, уважаемые читатели!



Сегодня предлагаем вам прочесть фрагмент четвертой части статьи «Многозадачность во встроенном приложении».



Приятного чтения! Прощаемся с Вами до следующего выпуска.


С уважением, команда Клуба.


Оглавление



Совершенствуем объектную модель на языке C


Разработка встроенного кода в наших проектах до сих пор проходила по такой схеме: сначала строилась объектная модель, документировалась в виде диаграмм UML, а затем воплощалась в коде на языке C. Объектно-ориентированный подход к проектированию при правильном использовании позволяет получить высокое качество проекта. Однако язык программирования C, который мы используем для создания кода для микроконтроллеров, не является объектно-ориентированным, поэтому нам приходилось использовать некоторые ухищрения для имитации объектных средств. Так, например, вместо классов мы использовали модули, вместо методов — функции с именами вида «ИмяМодуля_имяФункции», роль интерфейсов у нас выполняли заголовочные файлы и т.д. Механизм инкапсуляции имитировался посредством видимости «закрытых» членов в пределах файла. Вплоть до настоящего момента такой подход достаточно хорошо работал: мы успешно выполнили на его основе простейший завершенный проект и начали другой, который до сих пор также продвигался успешно.

Однако я неспроста сделал акцент на словах «простейший» и «до сих пор». Вследствие этой простоты нам не требовалось несколько экземпляров «объектов», вполне хватало одного (один канал таймера, один ключ управления источником света и т.д.). Если продолжить параллель с объектной парадигмой, мы научились работать со статическими членами класса (фактически реализовали паттерн проектирования Monostate). Но если мы ограничимся лишь таким примитивным уровнем, все наши усилия, направленные на получение качественного кода посредством TDD, пропадут впустую: несколько строк незатейливого кода можно было бы отладить и обычным методом «коекакинга», а путь к более сложным устройствам для нас будет закрыт.

В спецификациях нашего текущего проекта указано, что устройство должно иметь два независимых канала, которые ведут себя сходным образом, но имеют различные количественные характеристики. Конечно, мы могли бы продублировать код методом Copy-Paste, а затем внести необходимые поправки; полученный в результате этих действий код, возможно, даже работал бы некоторое время (до первой исправленной ошибки или модификации кода, которая будет внесена в одну из копий кода, в то время как другая окажется благополучно забытой)... Ну, или по крайней мере создавал бы видимость работы. Впрочем, раз уж мы взяли курс на разработку качественного кода, этот вариант нам никак не годится. Вот если бы мы могли создавать несколько экземпляров наших «объектов», и каждый из них мог иметь собственное состояние, сохраняя при этом общее для класса поведение...

В общем-то в этом пожелании нет ничего нереального; вспомним, что в том же C++ class — это фактически синоним struct, а структуры у нас в C имеются. Структура вполне пригодна для хранения состояния объекта, то есть набора значений его членов-переменных. Правда, есть одна проблема: в C отсутствуют средства управления видимостью полей структуры. Область видимости поля структуры совпадает с областью видимости самой структуры; иными словами, если в структуре есть некоторое поле и эта структура доступна нам в некоторой точке программы, то и само поле также является полностью доступным. Получается, об инкапсуляции членов-переменных можно забыть, поскольку все они фактически имеют видимость public? Это очень плохая новость, поскольку без инкапсуляции сам объектный подход полностью утрачивает для нас привлекательность: объект, который не в состоянии гарантировать собственную целостность, не может быть основой для качественного кода.

Не знаю, как вам, уважаемые читатели, но мне очень не хотелось бы бросать объектно-ориентированное проектирование встроенных систем, не успев завершить даже столь скромный проект, и возвращаться к старой структурной парадигме. Это было бы большим шагом назад. Поэтому перед тем, как двинуться дальше в разработке проекта, я сделаю небольшую паузу для усовершенствования методики объектного программирования на языке C.


Постановка задачи


Прежде всего наметим цель. Понятно, что реализовать полную объектную парадигму средствами процедурного языка вообще и C в частности — задача совершенно нереальная, поскольку в противном случае попросту не было бы необходимости реализовывать объектные расширения процедурных языков, которые в конечном итоге развивались в самостоятельные языки (например, Object Pascal из Pascal или C++ из C).

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

Как уже было упомянуто выше, класс C++ выглядит до определенной степени сходно со структурой C. И класс, и структура имеют набор переменных-полей (членов), совокупность значений которых определяет состояние класса/структуры. Однако в C++ есть возможность задать область видимости поля при помощи ключевых слов public, private и protected. В C такой возможности нет: если уж мы получили доступ к структуре, нам автоматически становятся доступны все ее поля. Поскольку у нас нет возможности ограничить видимость отдельных полей структуры C, остается единственный выход: спрятать структуру, хранящую состояние объекта, от внешнего доступа полностью.

С точки зрения хорошего тона программирования, открытый доступ к членам класса весьма нежелателен, поскольку позволяет произвольно менять значения полей извне класса. Это не позволяет поддерживать целостность состояния класса; фактически такой класс ничем принципиально не отличается от структур C или аналогичных конструкций других процедурных языков программирования. Поэтому вариант с сокрытием структуры состояния объекта полностью будет вполне приемлемым для практических нужд и ничуть не ухудшит чистоту нашего кода.

Второй важный аспект, отличающий классы C++ от структур C, состоит в том, что классы, помимо данных, включают также набор собственных функций (методов), которые имеют полный доступ к переменным состояния объекта (структура C может включать лишь поля-данные). Открытое подмножество методов определяет интерфейс класса, то есть набор операций, которые объект данного класса способен выполнять. Грамотно спроектированный класс позволяет изменять свое состояние лишь посредством обращения к интерфейсу.

Итак, на данном этапе проекта нас полностью устроило бы решение, которое позволило бы полностью скрыть состояние объекта от клиентов. Все взаимодействие клиента с объектом должно производиться посредством обращения к методам объекта.

Вопросы наследования и полиморфизма пока оставляем открытыми, поскольку практическая потребность в этих механизмах в рамках наших проектов пока не возникала (да и, учитывая довольно скромные задачи, решаемые встроенными микроконтроллерами, будет возникать не так уж часто). Вернемся к ним при необходимости.

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

Попробуем найти решение поставленной задачи.


Сокрытие состояния объекта


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

Попробуем воспользоваться одним средством языка C, которое до сих пор оставалось невостребованным нами. В стандарте ISO/IEC 9899:1999(E) оно называется «incomplete structure or union type» (6.7.2.3/7, стр. 107). Мы можем включить в программу объявление вида:


...



Полностью прочитать статью можно на нашем сайте, в разделе «Инструменты и технологии проектирования ПО».



В избранное