Продолжая тему приложения принципов кибернетики в программировании, начатую в предыдущей статье [1], и оставаясь в рамках стандарта ISO/IEC 14882-1998, покажем, как идеи децентрализованной инициативы в организации взаимодействия компонентов системы (равноправного начала кибернетики) отражаются в языке программирования C++ при решении типовой задачи регулирования.
Формулировка решаемой задачи остается прежней – поддерживать давление в трубопроводе на определенном уровне. Согласно принципам, изложенным в статье [2], система примет вид:
Рис. 1. Кривая сложности системы и «объектной ориентированности»
В результате получаем три базовых сущности – класс контроллера (C), класс насоса (P), класс датчика (S), образующих единую систему (System). При этом контроллер считается всегда пассивной сущностью – может лишь отвечать на обращения других компонентов к нему, а остальные сущности считаются соответственно активными. Взаимодействие компонентов происходит в два этапа – на первом этапе одна из активных сущностей инициирует взаимодействие с контроллером, в результате которого последний обновляет свое состояние. На втором этапе, строго следующим за первым, контроллер отвечает на отклик от активного компонента, выдавая ему в ответ управляющее воздействие. Собственно алгоритм работы контроллера будет иметь формулировку:
В момент получения отклика датчика системы – обновить модель состояния объекта, в момент получения отклика от исполнительного устройства системы – возвратить ему управляющий сигнал в соответствии с ошибкой между целевым состоянием и состоянием, хранимым моделью.
Но прежде, чем продолжить проектирование следует сделать небольшое пояснение к моменту идентификации сущностей системы. Это чрезвычайно важный вопрос, т.к. понятие класс в объектной системе играет ту же самую роль, что и понятие функция в структурной композиции. Если функция – мера разделения времени, то класс – мера разделения ресурса [2]. Соответственно как функция является технологическим элементом структурной системы, так и класс играет ту же самую роль технологического элемента, но уже для объектной системы [1].
Вопрос идентификации функции в программе решается программистами с конца 60-х годов прошлого (XX) века [3], поэтому мы, программисты, часто вводим функции в программу интуитивно, даже не осознавая тех предпосылок, которые собственно и заставляют нас определять относительно независимую в ней сущность. Однако предпосылки эти существуют, и главной из них является наличие известных ограничений на аппаратные ресурсы. Аппаратные ограничения в конечном итоге влияют на длину массива данных, которым манипулирует функция. А, как известно, чем более определена структура данных, тем более определен алгоритм функции. Можно много говорить о прогрессе современной вычислительной техники, но, как и 40 лет назад, процессоры позволяют манипуляции над данными, только лишь, так или иначе, загружая их из оперативной памяти в память регистровую. Размер же регистра для любой физически существующей машины является известной конечной величиной и, как правило, не допускающей динамического роста. Поэтому, как и 40 лет назад, структурная методология остается актуальной в программировании, т.к. элементарные типы любого современного языка программирования, так или иначе, связанны с их аппаратным представлением в виде регистровых переменных.
Следует отметить, что в профессиональной среде разработчиков часто распространено мнение о первичности алгоритма перед данными. Однако также следует помнить, что проектирование системы может вестись на различных уровнях ее представления. А для уровня выражения программы в виде инструкций языка программирования первичным все же является вопрос о входных и выходных данных всей программы в целом. Действительно, какой бы метод обработки информации не избрал бы программист, первым делом все же требуется перевести информацию в данные, т.е. выполнить сопоставление числовому значению некоторого количественного или качественного смысла. Именно с подобного сопоставления и началась история математики вообще, не говоря уже о машинных вычислениях.
Естественно, что для объектной методологии также существуют предпосылки идентификации классов. Как упоминается в [2], основной причиной организации подсетей в равноправных системах является стремление к нормализации плотности потока событий в рамках подсети. Поскольку между подсетью и понятием класса проводится прямое соответствие, то из утверждения выше следует, что каждый класс имеет уникальную для него частотную характеристику. Чтобы лучше раскрыть смысл последнего утверждения можно провести аналогию с поэзией. Так, например, сборник стихов поэта может быть разделен им самим на поэтические циклы. Циклы в свою очередь могут составлять несколько конкретных образов. Один образ может охватывать несколько стихов, написанных различными тропами. Также часто встречается и прямое соответствие один образ – одно стихотворение. Естественно, что разные стихотворения одного и того же поэта имеют характерный, уникальный только для этого человека почерк. Это же утверждение справедливо и для образа и для цикла и для сборника. Но важно так же, что и почерки отличаются между уровнями формы – образом, циклом или сборником. Так, например, почерк поэтического цикла мы привыкли называть мотивом. При этом в границах одной и той же формы их может быть несколько, но доминантный мотив остается постоянным. То же самое можно и сказать про частотную характеристику класса – это своеобразный, уникальный почерк, появление которого связано, прежде всего, с тем, что окружающий нас мир не идеален и соответственно класс, у которого плотность потока событий составляет постоянную во времени величину, не может существовать. Иначе мы допускаем грубейшую ошибку на стадии проектирования – выход за пределы условий, допускающих организацию системы в рамках равноправного начала. Равноправное начало по определению не допускает строгой предсказуемости возникновения событий [2].
Итак, мы идентифицируем три класса-сущности, включаемых в контекст системы – класс контроллера – CPressureController, класс насоса – CPump и класс датчика – CPressureSensor, т.к. частотные характеристики событий, генерируемых множеством экземпляров каждого из классов, будут различны. Иными словами классы будут иметь различные частотные почерки.
Теперь мы имеем представление о том, какие сущности могут существовать в рамках контекста системы. Однако для включаемых классов требуется установить среди них единый протокол взаимодействия друг с другом. Как известно, это достигается с помощью использования комбинатора наследования, [2]. Таким образом, приведем включаемые классы к общему знаменателю.
class CPump : public CSysUnit { /*…*/ };
class CPressureSensor : public CSysUnit { /*…*/ };
class CPressureController : public CSysUnit { /*…*/ };
Для пояснения смысла класса CSysUnit сделаем небольшое отступление от процесса реализации включаемых в систему классов и обратимся к сущности самой системы – инфраструктуры функционирования компонентов. Подобно тому, как функция main() для структурного стиля является контекстом построения системы на централизованных началах, так и класс система (CSystem), для объектно-ориентированной композиции будет тем связующим контекстом, который собирает компоненты системы в единое целое. Это означает, что объект класса системы будет поддерживать список участников взаимодействия и предоставлять возможность распространения сообщений от инициаторов в пределах списка. Такое решение является частным случаем шаблона проектирования «композитор» [4] в том смысле, что не поддерживает рекурсию включения, поскольку поставленная перед нами задача относительно проста. В общем случае, шаблон «композитор» для ООП является тем же, что и иерархия вызовов функций в ходе выполнения программы, организованной в структурной методологии [1]. Если для структурной методологии доминирующим комбинатором организации системы являлся комбинатор следование, то для объектной системы – комбинатор инкапсуляция, который упрощенно можно представлять себе в виде отношения часть-целое (отношение включения). Именно это отношение и выражается классами-контейнерами стандартной библиотеки C++. Однако построение списка участников с помощью контейнера-последовательности, например, std::vector<> требует, чтобы все элементы контейнера наследовали одному общему типу. Для этой цели в проект введен абстрактный класс CSysUnit, определяющий требования системы ко включаемым в ее состав компонентам.
class CSysUnit {
public:
virtual ~CSysUnit() = 0;
virtual bool Accept( CSysUnit * pSender ){ return false; } };
// Псевдоним для типа контейнера компонентов системы.
typedef std::vector< CSysUnit * > subnet_t;
Данный класс определяет в контексте метода Accept() реакцию по умолчанию для каждого компонента системы, на общесистемную широковещательную рассылку сообщения одним из ее участников. Если метод не переопределен в классе-наследнике, то сообщение будет им отвергаться. Отметим также, что метод Accept() объявлен виртуальным. Это позволяет с уровня класса CSysUnit распространять сообщения каждому участнику взаимодействия в системе, независимо от того, какую последующую ступень он занимает в иерархии наследования. Таким образом, в объектной системе задействуется последний комбинатор композиции – полиморфизм. Метод принимает указатель на объект-отправитель сообщения. Это позволяет реализовать непосредственный отклик на обращение инициатора, не прибегая к широковещанию [5]. В зависимости от класса, в котором реализуется переопределенный метод Accept(), производится динамическое приведение типа отправителя к ожидаемому типу партнера взаимодействия. Если это приведение проходит успешно, то объект производит обработку сообщения, получая по мере необходимости данные с помощью методов объекта-инициатора.
При такой реализации информация о зависимостях каждого компонента системы от ее остальных участниках локализуется в рамках независимых классов, что позволяет легко расширять систему по мере необходимости, т.к. изначально ни один из компонентов системы не имеет полного представления обо всех существующих в системе классах объектов.
Следует также обратить внимание, что деструктор, объявленный чистой виртуальной функцией, делает класс абстрактным – не дает создавать объекты непосредственно этого класса, требуя реализации его в классе-наследнике.
Теперь, когда сущности системы идентифицированы и определены взаимоотношения между ними, необходимо выполнить их реализацию. Для этого, прежде всего, требуется составить список прототипов функций-членов для каждого из классов. Чтобы найти решение этой проблемы необходимо вновь возвратиться к двум этапам взаимодействия компонентов системы. На класс CSystem возлагается обязанность по разрешению потенциальных конфликтов одновременного обращения компонентов системы к контроллеру. В стандартной библиотеке языка C++ нет сущности, реализующей протокол взаимодействия CSMA/CD, требуемый для разрешения подобных конфликтов [2]. Как следствие, в программу вводятся классы исключений ELongBcast и EMediumBusy – исключительная ситуация превышения максимальной длительности распространения сообщения компонентам системы и исключительная ситуация коллизии при попытке узла начать вещание соответственно. Данные классы играют вспомогательную роль в реализации инфраструктуры системы.
class ELongBcast : public std::runtime_error {
public:
explicit ELongBcast( const std::string& what_arg ) : std::runtime_error( what_arg ){}};
class EMediumBusy : public std::runtime_error {
public:
explicit EMediumBusy( const std::string& what_arg ) : std::runtime_error( what_arg ){}};
Непосредственно функции-члены класса CSystem должны предоставлять три возможности.
Возможность производить передачу (send) сообщений в рамках системы.
Возможность производить прием (receive) сообщений в рамках системы.
Возможность управлять параметрами приемопередачи сообщений.
// Числу сопоставляется смысл времени, измеряемого в секундах.
typedef std::size_t sec_t;
// Псевдоним для типа, используемого далее при индексации.
typedef int indx_t;
class CSystem {
public:
// Передача сообщений
void Broadcast( CSysUnit * pSender ) throw( ELongBcast, EMediumBusy );
// Подписка на прием сообщений
indx_t AddNode( CSysUnit * pNode );
bool DelNode( indx_t iIndx ) throw( EMediumBusy );
size_t GetNodeCount();
CSysUnit * GetNodePtr( indx_t iIdx ) const;
template< class TResult >
TResult * FindNodeOfClass( TResult * pResult ) const;
// Конструктор класса
explicit CSystem( sec_t uiMaxBcastTm = 10, sec_t uiMaxMemWait = 30, std::size_t uiMaxMemAtmts = 6 ); // Управление приемопередачей
bool SetConstraints( sec_t uiMaxBcastTm, sec_t uiMaxMemWait, std::size_t uiMaxMemAtmts );
bool GetConstraints( sec_t * puiMaxBcastTm, sec_t * puiMaxMemWait, std::size_t * puiMaxMemAtmts ) const;
private: // Список подписчиков
subnet_t m_NodeList; };
Список возможностей, которые должен реализовывать класс, как правило, называют его областью ответственности или ролью в системе. Роль же класса проявляется при его взаимодействии с партнерами. Как упоминалось ранее, взаимодействие проходит в два этапа (рис.1). С точки зрения инициирующего компонента системы его можно представить в виде одного вызова функции, где первый этап будет соответствовать непосредственно передаче в нее управления, а второй – возврату из нее [1]. Однако, поскольку построение системы на равноправном начале подразумевает, что инициатор не знает точного адреса того объекта, к которому обращается [2], то применяется прием широковещания, т.е. доставка сообщения всем компонентом системы. Именно эту главную задачу и выполняет класс CSystem, являясь своего рода посредником (шаблон «посредник» [4]) между клиентом – инициатором взаимодействия и сервером – классом контроллера.
Естественно, раз есть механизм широковещательной рассылки сообщения, то должен быть и механизм подписки на данную рассылку, а также механизм управления ее характеристиками.
Таким образом, инициатор взаимодействия производит своего рода установление соединения и его разрыв, в контексте которого адресат соединения способен выполнять взаимодействие с инициатором с помощью механизма обратного вызова, упоминавшегося ранее при обсуждении комбинатора полиморфизма. Открытые интерфейсы классов CPressureSensor и CPump определяются именно в контексте обратных вызовов из класса CPressureController – это соответственно возможность получить текущее показание датчика и возможность управлять состоянием насоса. Интерфейс же самого класса контроллера определяется в контексте взаимодействия с ним пользователя системы. В нашем случае он заключается в предоставлении возможности установки целевого значения давления для процесса регулирования.
class CPump : public CSysUnit {
public:
explicit CPump( const char * szDevPath ) throw( std::invalid_argument );
~CPump();
// Управление насосом
bool TurnOn();
bool TurnOff();
// Метод опроса состояния
bool IsTurnedOn();
private:
std::fstream m_Stream;
CPump(); };
// Числу сопоставляется смысл величины давления, измеряемого в килопаскалях.
typedef double KPa_t;
class CPressureSensor : public CSysUnit {
public:
// Метод опроса состояния
KPa_t GetValue();
explicit CPressureSensor( const char * szDevPath ) throw( std::invalid_argument );
~CPressureSensor();
private:
std::fstream m_Stream;
CPressureSensor(); };
class CPressureController : public CSysUnit {
public:
explicit CPressureController( KPa_t dSetpntP = 0.00 );
~CPressureController();
// Метод обработки запросов активных компонентов
virtual bool Accept( CSysUnit * pSender ) {
bool fResult = false; if( pSender != NULL ){ this->OnSensorPulse( dynamic_cast< CPressureSensor * >( pSender ) ); this->OnPumpPulse( dynamic_cast< CPump * >( pSender ) );fResult = true; } return fResult; }
// Управление уставкой
KPa_t GetSetpointP() const;
void SetSetpointP( KPa_t dNewP );
private:
KPa_t m_dSetpointP;
KPa_t m_dActP;
// Методы обновления состояния модели
void OnSensorPulse( CPressureSensor * pSensor ) {
if( pSensor != NULL ) m_dActP = pSensor->GetValue(); }
void OnPumpPulse( CPump * pPump ) {
if( pPump != NULL ){ if( m_dActP < m_dSetpointP ){ pPump->TurnOn();}
else{ pPump->TurnOff();} } // if not NULL }// End of function };
Следует обратить внимание на реализацию функции-члена Accept() в классе CPressureController, которая отражает изложенные выше идеи относительно взаимодействия компонентов системы.
Еще одним важным моментом является включение объектов std::fstream в состав классов насоса и датчика. Подобно тому, как в языке C структурная композиция оканчивается функциями файлового ввода-вывода [1], так и в C++ объектная композиция завершается классом файлового ввода-вывода. Стандартная библиотека C++ определяет практически ту же самую функциональность, что и стандартная библиотека C, местами расширяя и дополняя ее. Однако если в C она выражалась в виде непосредственно множеств функций, то в C++ функции формируются вокруг классов, позволяя выражать также и взаимодействие программы с окружающим ее миром в терминах объектной композиции. Отсутствие такой возможности, как правило, приводит к интенсивному включению в качестве полей класса переменных, не связанных непосредственно с ролью данного класса, что в свою очередь ухудшает качество инкапсуляции. В результате программист будет стремиться игнорировать часть событий от инициаторов, что противоречит самому смыслу построения системы на равноправных началах [2]. Соответственно, если в структурном стиле программа представлялась в качестве элементарных перемещений блоков памяти, объединенных комбинаторами следование, альтернатива, цикл, то в объектном стиле программа есть, скорее, совокупность способных к росту агрегатов памяти, объединенных комбинаторами инкапсуляция, наследование и полиморфизм. Здесь следует пояснить, что в слово блок памяти вкладывается смысл совокупности смежных байтов памяти, непрерывного сегмента, размер которого, как правило, фиксирован. Иными словами – это массив данных. Агрегат памяти – это более сложная структура, способная состоять из множества непрерывных сегментов, расположенных в общем случае по несмежным адресам. Но, все же, главное свойство, которое делает систему работающей – это способность роста сегментов прямо во время выполнения программы. Объектно-ориентированная программа в памяти образует своего рода миниатюрную файловую систему. Объект рассматривается программистом подобно тому, как простой пользователь рассматривает файл в процессе передачи, – в виде единой сущности. Однако, как известно, физически файл сохраняется на диске в виде совокупности сегментов, не обязательно смежных друг другу. Именно это свойство сохранения целостности при распределенной структуре позволяет файлу увеличивать свой размер во времени, близком к реальному, совместно с другими файлами.
Возвращаясь к насосу и датчику, следует отметить, что им необходима привязка к файлам-образам физических устройств в момент создания объектов, иначе выполнение предоставляемых им функций-членов теряет смысл или же влечет к реализации в каждой функции-члене дополнительных проверок и более интенсивной эксплуатации исключений. Как следствие, конструкторы по умолчанию для данных классов объявлены закрытыми членами.
Реализация функций-членов классов при качественно выполненной инкапсуляции, как правило, тривиальна (полный листинг программы, http://clck.ru/d/YJMSHlJ415hpj).
Однако чтобы система заработала, недостаточно только лишь определить классы, из которых она состоит, необходимо создать с их помощью совокупность объектов и связать последние между собой. Кроме того, требуется, чтобы компоненты системы за исключением объекта-контроллера представляли собой активные сущности. Это требование может быть реализовано несколькими возможными способами. Первый способ подразумевает обработку прерываний от связанных с объектами устройств, что выходит за рамки ISO/IEC 14882-1998. Второй способ подразумевает создание отдельного потока исполнения для каждого из активных объектов, в котором объект производит циклический опрос связанного с ним устройства и генерацию соответствующих событий для контроллера. К сожалению, стандартная библиотека C++, определяемая в ISO/IEC 14882-1998, не имеет механизмов управления потоками, поэтому и этот способ не подходит для поставленной задачи. Тем не менее, следует отметить, что стандарт ISO/IEC 14882-2011 уже определяет необходимый механизм и в будущем следует отдавать предпочтение именно ему, т.к. он позволяет реализовать неблокирующий ввод/вывод, критичный для отказоустойчивой объектной системы. В нашем же случае, поскольку ни один из способов выше не позволяет нам остаться в рамках стандарта, функция main() будет симулировать обращение к контроллеру от имени объектов, связанных с устройствами, что, увы, будет означать использование синхронного (блокирующего) ввода/вывода. При этом важно, чтобы соблюдался принцип равноправия (т.к. система основывается на равноправных началах), поэтому применяется имеющийся в C механизм генерации псевдослучайных чисел, обеспечивающий случайный порядок обращений к контроллеру от имени устройств.
int main( int argc, char* argv[] ){
using namespace std;
char * szPumpDevPath = NULL;
char * szSensorDevPath = NULL;
char * szStatePath = NULL;
if( argc == 4 ){
szPumpDevPath = argv[ 1 ];
szSensorDevPath = argv[ 2 ];
szStatePath = argv[ 3 ];}
else { cout << "Arguments format:" << endl; cout << "\tPControl <PumpDevPath> <SensorDevPath> <AppStatePath>" << endl; exit( 0 ); }
try{
// Инстанцирование компонентов системы
CPump PumpDev( szPumpDevPath );
CPressureSensor SensorDev( szSensorDevPath );
CPressureController Controller;
CSystem Mediator;
// Объединение совокупности компонентов в единую систему
Mediator.AddNode( & PumpDev );
Mediator.AddNode( & SensorDev );
Mediator.AddNode( & Controller );
g_pSystem = & Mediator;
// Загрузка сохраненного состояния системы с диска
CNVolStateImage NVStImage;
g_pSaveState = SettingsLoad( & g_uiMaxRndDelay, & Controller, & Mediator, & NVStImage, szStatePath );
// Установка обработчика пользовательских прерываний
signal( SIGINT, & OnInteract );
if( g_pSaveState == NULL ){
// Инициализация значениями по умолчанию
g_pSaveState = SettingsInit( & NVStImage, szStatePath, & g_uiMaxRndDelay, & Controller, & Mediator ); raise( SIGINT );}
// Основной цикл программы
while( ! g_fDone {
DelayRandom( g_uiMaxRndDelay );
size_t uiNodeCount = Mediator.GetNodeCount();
assert( uiNodeCount > 0 );
indx_t iNodeIdx = rand() % ( uiNodeCount - 1 );
CSysUnit * pSender = Mediator.GetNodePtr( iNodeIdx );
Mediator.Broadcast( pSender ); } }
catch( exception * pE ) {
ReportError( pE ); }
return errno; }
В данной выдержке кода следует обратить особое внимание на основной цикл программы. Как уже отмечалось ранее, система организуется на равноправных началах, т.е. в любой момент времени ее функционирования любой из компонентов системы и в неопределенном порядке может инициировать взаимодействие с остальными ее компонентами. Однако ранее упоминалось, что объект контроллера мы считаем всегда пассивным. Это небольшое отступление от общего принципа построения архитектуры, позволялось на ранней стадии проектирования системы, до тех пор, пока мы оставались только лишь в пределах комбинаторов инкапсуляция, наследование и полиморфизм. Но, поскольку, необходимость реализации методов заставила нас прибегнуть и к следованию, альтернативе и циклу, то система уже является не просто клиент-серверной, а полноценной системой однорангового взаимодействия [2]. Однако, поскольку ни один другой компонент кроме самого контроллера не обрабатывает широковещательные сообщения от других узлов, то по факту широковещание от имени самого контроллера будет происходить «вхолостую». Тем не менее, наличие единого метода для инициации взаимодействия от имени компонента системы значительно упрощает алгоритм основного цикла программы.
Вследствие того, что стандарт языка C++ не вносит никаких дополнений и изменений относительно сигналов взаимодействия с терминалом пользователя в C, мы вынуждены производить их обработку в структурной методологии, независимо от существующей объектной композиции. Это достигается путем публикации в глобальной области видимости указателей на объект система (Mediator) и объект – энергонезависимое состояние (NVStImage). Причем глобальные переменные, хранящие эти указатели, используются только в рамках функций main() и OnInteract(). Очевидно, что смешение стилей вносит некоторую путаницу в исходный код программы, что ж, мы живем в несовершенном мире, и проблема полноценного стандартного объектно-ориентированного интерфейса взаимодействия с пользователем в C++ актуальна уже многие годы.
Поскольку взаимодействие с пользователем организуется в рамках структурного стиля программирования, то в данном случае применимы те же самые идеи, что были изложены в [1]. Однако в отличие от регулятора на централизованных началах, где каждая смена уставки влияла также и на параметры определяющие поведение системы (длительность одного непрерывного цикла регулирования), то в регуляторе, построенном на равноправных началах, поведение системы в процессе регулирования остается относительно стабильным. Поэтому было принято решение вынести часто изменяемые и редко изменяемые параметры в различные пользовательские диалоги. Это потребовало в функции OnInteract() выводить меню, дающее пользователю выбрать необходимый ему диалог взаимодействия с системой. Детали реализации этого решения, а также реализацию механизма сохранения состояния приложения на диске после смены параметров можно найти в полном листинге программы (http://goo.gl/0JY1K).
В заключение следует сказать несколько слов о будущем структурного и объектного стиля программирования. Очевидно, что не может существовать единственного совершенного метода для решения всех проблем, возникающих перед программистом, как и не может существовать такого языка программирования позволяющего решать любую задачу максимально эффективно [6]. Мир вокруг нас не описывается единственной формулой, просто потому что точность наших знаний о нем не является исчерпывающей. Однако данное обстоятельство не исключает возможности решения не всех, но большей части проблем в рамках одного идейного базиса, важно лишь знать границы его применимости. Язык C++ является на сегодняшний день наиболее универсальным языком программирования, позволяющим создавать программы как в рамках структурного подхода, вследствие сохранения совместимости с языком C, так и в рамках объектного подхода, предоставляя мощную и согласованную поддержку средств объектно-ориентированной композиции. Такая непротиворечивость языка и его мощь основываются, прежде всего, на том, что он изначально разрабатывался для решения задач телекоммуникаций [6], которые наиболее полно формулируют те кибернетические принципы, которые так или иначе могут закладываться в фундамент любой технической системы. Понимание данных принципов необходимо для дальнейшего развития программирования, как сферы производственной деятельности, и за последние годы здесь наметился положительный сдвиг. Все чаще в Интернет-сообществе разработчиков можно встретить высказывания о несовершенстве объектного подхода [7], и это означает, что программисты постепенно, пускай даже на интуитивном уровне, начинают понимать о наличии границ применимости структурного и объектного подходов. После их идентификации, несомненно, будет найден еще один способ организации систем, который начнет новый виток развития информационных технологий.
В рамках современной России вследствие постоянного дефицита как материальных, так и трудовых ресурсов, применять объектный стиль в программировании необходимо с должной осторожностью и пониманием сути решаемой задачи. Если сравнить листинги программ регулятора на централизованных началах (http://goo.gl/s2oJG) и регулятора на равноправных началах (http://goo.gl/0JY1K), несложно отметить, что объектный подход гораздо более трудоемок. Кроме того, сам смысл его применимости – разрешение проблемы при дефиците времени за счет избытка ресурсов [2]. Однако он позволяет решать задачу для требований, выходящих за рамки структурного подхода, и должен быть избран, если структурный стиль принципиально не подходит для решения поставленной задачи [8].
Литература:
Миненков А.М. Построение алгоритма регулятора давления в методологии структурного программирования [Текст] / А.М. Миненков, В.С. Усатюк // Молодой ученый. – 2012. – №5. – С. 92-98.
Миненков А.М. Кибернетические начала в методологиях структурного и объектно-ориентированного программирования [Текст] / А.М. Миненков, В.С. Усатюк // Молодой ученый. – 2012. – №5. – С. 98-104.
Дал У., Дейкстра Э., Хоор К. Структурное программирование. М.:Мир, 1975. – с.7 – 97.
Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. – СПб.: Питер, 2001. – 368 с.
Миненков А.М. Методология объектно-ориентированного программирования на примере модели сетевых протоколов OSI [Текст] / А.М. Миненков, В.С. Усатюк // Молодой ученый. – 2011. – №12. Т.1. – С. 86-91.
Страуструп Б. Язык программирования C++. – М.: Бином-Пресс, 2007. – 1104 с.
Торвальдс Л. [Электронный ресурс]: [сайт]. – Режим доступа: [Электронный ресурс]: [сайт]. – Режим доступа: http://harmful.cat-v.org/software/c++/linus
Kumar V. Autonomous Agile Aerial Robots [Электронный ресурс]: [сайт]. – Режим доступа: http://www.youtube.com/watch?v=4ErEBkj_3PY