Сложность разработки программного обеспечения (ПО) выдвигает задачу экономии человеческих ресурсов, затрачиваемых на этот процесс. Интеллектуализация всех видов деятельности человека, связанных с вычислительной техникой - универсальный ключ к решению этой задачи. В данной статье излагается подход к интеллектуализации трех аспектов взаимодействия человека с ЭВМ: языка, интерфейса и технологии разработки программ. Подход основан на привлечении объектно-ориентированной модели реального мира в процесс программирования.
В традиционном промышленном подходе к разработке ПО отправной точкой служат требования заказчика (пользователя). Процесс разработки разбивается на последовательные этапы: анализ, проектирование, программирование, документирование, тестирование, сопровождение. Этапы выполняются отдельными людьми или коллективами в соответствии с четко определенными интерфейсам между этапами. Такой подход позволяет упростить работу на каждом этапе, ввести некоторую параллельность их выполнения, и за счет этого повысить эффективность разработки и качество готового программного продукта.
Существенным недостатком традиционного подхода является отсутствие гибкости: решения, принятые на ранних этапах, не могут быть изменены без значительных затрат временных и человеческих ресурсов и понижения качества окончательного продукта. Тот же недостаток проявляется и при попытке внести изменения в отдельную часть сложной программы - как правило, приходится вносить изменения и в другие части.
Рассмотрим этап проектирования сложной программной системы. Применяемые здесь методы декомпозиции системы на модули навязываются характером используемых на следующем этапе средств и методов программирования на базе традиционных языков типа Фортран, Паскаль, Си. А те, в свою очередь, носят отпечаток фон-Неймановской архитектуры ЭВМ.
Типичная статическая структура системы включает главный модуль с общими глобальными данными и ряд подчиненных ему, но относительно независимых друг от друга, модулей с локальными данными. Динамическая структура состоит из последовательности вызовов главным модулем подчиненных модулей. Некоторые дополнительные средства структуризации, такие, как рекурсия, итерация, блочная структура, принципиально не меняют представленную здесь схему декомпозиции.
Очевидно, что изменение структуры системы, например, в результате обнаружения ошибки в логике взаимосвязей между модулями, влечет за собой перепрограммирование затронутых модулей, повторное тестирование и отладку. Это может внести ошибки в реализацию других модулей, связанных с первыми через глобальные данные, и привести к необходимости нового пересмотра общей структуры системы. Причем каждая итерация в цикле жизни программной системы обходится дорого, особенно в тех случаях, когда разные этапы выполнются разными людьми.
Конечный пользователь разрабатываемого ПО, его заказчик, напрямую заинтересован в более гибком подходе. Требования к системе могут измениться по многим причинам: накопленному опыту работы с ней, изменением конфигурации аппаратных средств, переносом на другую ЭВМ. Таким образом, нужен подход, позволяющий осуществить быструю перенастройку системы производства ПО, иными словами, создание нечто вроде гибкого автоматизированного производства (ГАП) ПО.
Объектно-ориентированный подход к программированию состоит в повышении степени интеллектуальности методов и средств общения пользователя с ЭВМ за счет наделения их свойствами, присущими человеческому общению, поведению человека в реальном мире.
Объектно-ориентированнную модель реального мира можно представить в следующим образом. Реальный мир "заселен" объектами. Объект обладает свойствами, поведением, текущим состоянием. Между объектами существуют отношения, например, объекты с одинаковыми свойствами и поведением относятся к одному классу. Объекты одного класса могут иметь частично одинаковые свойства с объектами другого класса, наследуя эти свойства от него. Это соответствует статической структуре модели, причем она существенно богаче структуры типа "главный-подчиненные" и имеет древовидный или в общем случае сетевой тип.
Объекты взаимодействуют друг с другом путем передачи информации от одного объекта другому. Информация передается путем посылки сообщения. Объект, принимающий сообщение, реагирует на него особым, только ему известным образом. При этом он может послать сообщения другим объектам, получить от них ответы, изменить свое состояние и, наконец, вернуть ответ тому объекту, который послал ему сообщение. Объекты могут "порождаться" и "умирать". Важно здесь то, что действуют объекты, как правило, параллельно (одновременно). Это соответствует динамической структуре модели и она также значительно богаче традиционной последовательной структуры.
Впервые идеи об объектно-ориентированном подходе были воплощены в языке программирования Симула еще в середине 60-х годов [1], где были введены понятия:
В аппаратуре понятие объекта, как защищенной области памяти, содержащей взаимосвязанные команды и данные, было реализовано в известном 32-разрядном микропроцессоре iAPX-432 фирмы Интел [2].
Однако первой системой, полностью основанной на объектно-ори- ентированном подходе, считается система программирования Смолток [3].
Система Смолток, состоящая из интерпретатора объектно-ориентированного языка программирования высокого уровня и интерактивного графического пользовательского интерфейса (среды программирования), разрабатывается начиная с 1970 г. фирмой Ксерокс в исследовательском центре г.Пало-Альто (США). В настоящее время неофициальным стандартом является вариант языка и системы Смолток-80 [4,5].
Развитие объектно-ориентированных языков и систем идет по трем направлениям:
Это типичный процесс конкуренции эволюционного (1,2) и революционного (3) развития.
Хотя до сих пор нет точного критерия, по которому язык программирования мог быть отнесен к объектно-ориентированным, большинство исследователей выделяют следующие четыре необходимых свойства такого языка:
Скрытие реализации, по существу, означает тотальную, доведенную до высшей степени модульность, причем модулем может быть любой объект системы, от крупного компонента системы, например, компилятора, до обыкновенной числовой константы. Каждый такой модуль-объект имеет внешний интерфейс, известный за его пределами, и внутренюю реализацию, скрытую от других объектов.
Вид внешнего интерфейса и степень контроля за правильностью его использования зависит от языка. Впервые скрытие реализации было предложено в языке Модула и далее развито в языках Модула-2 и Ада. Здесь в качества внешнего интерфейса могут быть наборы взаимосвязанных данных и процедур, причем обеспечивается контроль за правильностью их совместного использования.
Скрытие реализации позволяет изменять отдельные модули, не затрагивая остальную часть системы, что облегчает внесение измене- ний и повышает надежность системы.
Абстрактные типы данных - средство, позволяющее с помощью встроенных в язык типов данных определять другие типы данных произвольной сложности, определять для них допустимые операции (процедуры, функции, декларации) и затем использовать новые типы по тем же правилам, что и обычные встроенные типы. В большинстве случаев абстрактные типы данных существенно опираются на скрытие реализации.
Впервые абстрактные типы данных были реализованы в языке Clu [6] в виде модулей, названных кластерами (отсюда и название языка). Дальнейшее развитие они получили в языках Ада и С++ [7].
Абстрактные типы данных дают возможность существенно повысить уровень языка, приблизив его к проблемной области, не выходя при этом за пределы синтаксиса и семантики языка.
Динамическое связывание означает, что значение любого имени в программе становится известно только в момент обращения к этому имени во время выполнения программы. Для данных это означает, что их адрес вычисляется во время обращения к ним по имени. Для процедур - вычисляется адрес машинной последовательности команд, которая будет выполнять данную процедуру.
В большинстве языков реализовано статическое связывание имени со значением - во время компиляции программы. Динамическое связывание чаще всего предполагает реализацию языкового процессора методом интерпретации. Впервые динамическое связывание в полной мере было реализовано в языке Лисп. Дальнейшее развитие этот метод получил в языках Форт, Лого, Пролог.
Преимущество динамического связывания состоит в наиболее полной реализации принципов скрытия реализации и гибкости, при котором изменение одного модуля не требует перекомпиляции взаимодействующих с ним модулей. Кроме того, уменьшается размер программы, поскольку нет необходимости в программировании условных операторов, контролирующих правильность соответствия типов во время выполнения программы. Очевидный недостаток - замедление скорости выполнения программы.
Наследование свойств позволяет строить новые объекты на базе существующих, указывая лишь, чем новые объекты отличаются от старых, т.е. либо добавляя новые свойства, либо переопределяя некоторые из старых свойств.
Впервые механизм наследования свойств был включен в язык Симула, дальнейшее развитие он получил в языках Ада, Objective C [8].
Наследование свойств позволяет сокращать размер программ за счет многократного использования частей различными объектами. Повышается также надежность и удобство внесения изменений в программы.
Рассмотрим характерные свойства объектно-ориентированных языков и систем программирования на примере системы Смолток-80.
Язык реализации системы и входной язык программирования для пользователя один и тот же - Смолток. На нем написана вся машинно-независимая часть, составляющая около 85% всей системы. Машинно-зависимая часть ради эффективности должна быть реализована на более низком уровне, в том числе и микропрограммно. При переносе системы на другую машину эта часть переписывается полностью.
Указанные две части системы получили название виртуального образа и виртуальной машины.
Виртуальный образ представляет собой откомпилированный образ написанной на Смолтоке части системы. Это встроенный набор классов и методов, за исключением так называемых примитивных методов, реализующих самый нижний уровень системы. Собственно говоря, выполнение любого метода в конечном счете сводится к последовательности вызовов примитивных методов.
В системе есть компилятор, переводящий программу с языка Смолток в промежуточный код стековой виртуальной машины (байт-код). Для того, чтобы была возможность вести отладку на уровне исходного языка, в байт-коде есть ссылки на соответствующее исходное представление программы, расположенное в файловой системе на диске.
Скомпилированные методы в виде байт-кодов интерпретируются виртуальной машиной. Виртуальная машина состоит из трех частей: интерпретатора, набора примитивных методов и системы управления памятью объектов.
Интерпретатор рассматривает скомпилированный метод как последовательность однобайтовых команд. Команда может иметь продолжение еще в нескольких байтах. Обычно байтовая команда работает со стеком, занося и читая из него операнды, представляющие собой указатели на объекты. Некоторые байтовые команды являются вызовами примитивных методов.
Поскольку в системе используется динамическое связывание, большая роль отводится распределению памяти под объекты. В больших программах могут порождаться тысячи и десятки тысяч объектов, поэтому необходимо своевременно освобождать память от ненужных объектов.
Сборка мусора в системе реализована методом счетчика ссылок. Если значение счетчика, связанного с некоторым объектом, становится равным нулю, объект переводится в список "мусора". В определенные моменты работы программы, и, в частности, всегда, когда свободная память исчерпывается полностью, выполняется процедура сборки мусора. Недостаток метода счетчика ссылок состоит в больших накладных расходах на ведение счетчиков. Сейчас предложены другие, более совершенные методы сборки мусора [9].
Объектно-ориенированные языки и системы программирования получили распространение в первую очередь в системах искусственного интеллекта. Их промышленное использование сдерживается недостаточной эффективностью реализаций на машинах традиционной архитектуры. Поэтому одним из путей развития таких систем является создание архитектуры ЭВМ, полностью ориентированной на объекты [10-12].
Можно указать на несколько направлений развития собственно объектно-ориентированных языков. Первое - это отражение в языке параллельности, присущей объектам реального мира. Смолток-80 и большинство подобных систем чисто последовательные, в которых объект, послав сообщение, ждет ответа. Разрешив объекту продолжать свои действия после посылки сообщения и после отправки ответа, мы вводим в язык параллельность со всеми сопутствующими проблемами диспетчеризации и синхронизации процессов. Уже существует несколько реализациий таких систем [13-15].
Второе направление - создание распределенных объектно-ориентированных систем, в частности, баз данных, предпосылкой которому является поддержка изолированности объектов и обмена сообщениями между ними [16,17].
Третье направление - введение множественного наследования и, соответственно, сетевой структуры классов. Это усложняет реализацию системы, но делает ее более универсальной [18]. Древовидная структура классов Смолтока-80 не позволяет ввести множественное наследование свойств, когда объект или класс имеет несколько суперклассов.
Copyright ©