На этом шаге мы рассмотрим более подробно объектно-ориентированную парадигму.
В статье [1] перечислены основные направления объектно-ориентированного программирования и языки программирования, поддерживающие каждое направление:
Направление... | ориентированное на сообщения | ориентированное на операции |
---|---|---|
ориентированное на классы/типы | Smalltalk, C++ virtuals | CLOS, Fortran/C++ overloads |
ориентированное на объекты | Self, Actors | Prolog, Data-driven |
Существуют различные мнения о том, является ли метод формирования объектов главным вопросом в объектно-ориентированном программировании, ставящим объект во главу угла. При рассмотрении объектно-ориентированных языков с позиций построения вычислительных моделей можно также считать, что существенным моментом является посылка сообщений как механизм организации вычислительного процесса [2, с.87-88].
Наше мнение по данному вопросу таково: ООП, ставящее объект во главу угла, является технологией программирования, а ООП, ставящее во главу угла посылку сообщений, - парадигмой программирования.
На этом шаге мы рассмотрим объектно-ориентированное программирование как технологию.
Прежде надо обсудить вопрос, кому и зачем нужна технология программирования.
В монографии [3, с.10] поставлена целевая проблема: "Все, что могут сделать профессиональные программисты для решения центральной задачи информационной технологии 80-х годов - формализации знаний - это попытаться создать типовую технологию (или спектр типовых технологических приемов, например по основным проблемным областям) для автоформализации знаний, т.е. разработать инструментальные средства, облегчающие непрограммирующим профессионалам процесс самостоятельной формализации их индивидуальных знаний."
Технология программирования нужна:
Несколько забегая вперед, отметим, что объектно-ориентированное программирование является прямым следствием усложнения современных приложений, например, наследование и инкапсулирование являются максимально эффективными средствами именно для борьбы с нарастающей сложностью программных проектов.
Как всякая технология, технология программирования направлена не на поднятие уровня лучших программистов на недосягаемую высоту, а, наоборот, на то, чтобы основная масса программистов могла производить хорошие программы. И развитие каждого элемента технологии (такого, например, как структурное программирование или ООП) проходит три стадии - творческую, идеологическую и инструментальную.
На творческой стадии выдающиеся программисты придумывают правила программирования для собственного пользования и с успехом их применяют. Так, например, было со структурным программированием в 60-х годах. Затем уже другие люди (чаще всего они даже не являются программистами) оформляют эти правила как идеологию. И, наконец, для удобного применения этой идеологии создается набор инструментальных средств, и новообращенные люди не всегда знают и понимают идеологию. Для них вся технология состоит в этих инструментальных средствах. Таким способом программисты среднего уровня и даже начинающие могут производить технологичные программы. (А программисты высокого уровня либо становятся еретиками, критикуя эти средства, а иногда и саму идеологию, либо разрабатывают следующую).
Мы выделяем три технологические парадигмы программирования:
Cтруктурное программирование (Structured Programming) - метод программирования, предусматривающий создание понятных, локально простых и удобочитаемых программ, характерными особенностями которых являются модульность, использование унифицированных структур следования, выбора и повторения, отказ от неструктурированных передач управления, ограниченное использование глобальных переменных [4-7].
Программирование с абстрактными типами данных и объектно-ориентированное программирование представляют собой новые подходы к структуризации программ, имеющие целью повысить качество программ (надежность, модифицируемость, познаваемость).
Языки программирования с абстрактными типами данных (АТД-языки) - языки программирования, поддерживающие технологию проектирования программ с использованием абстрактных типов данных, определяемых программистом. Одними из первых языков этого типа являются CLU, Alphard и Ada.
Абстрактный тип данных (Abstract Data Type) - тип данных, определенный только операциями, применимыми к объектам данного типа, без описания способа представления их значений [4-7].
Языки объектно-ориентированного программирования (Object-Oriented Languages) - языки программирования, программа на которых задается описанием поведения совокупности взаимосвязанных объектов.
Объекты обмениваются запросами; реагируя на полученный запрос, объект посылает запросы другим объектам, получает ответы, изменяет значения своих внутренних переменных и выдает ответ на полученный запрос. Механизм запросов в объектно-ориентированых языках интересен тем, что при выполнении запроса объектом непосредственно изменены могут быть только значения переменных этого объекта. В отличие от процедуры, которая описывает как должна выполняться обработка, запрос только определяет, что желает выполнять отправитель, а получатель точно определяет, что должно произойти. Объектно-ориентированная парадигма программирования вносит в программу модульность посредством абстракции данных и наследования и особенно хорошо подходит для ситуаций, когда имеется ясная иерархическая классификация объектов. Это позволяет избежать дублирования и локализовать описания механизмов работы с информацией. Одним из первых языков этого типа является Smalltalk [4-7].
Как всякая технология, объектно-ориентированное программирование является дисциплиной, которую вы должны "навязать" себе, используя предоставляемые языком средства.
Заметим, что концепция объекта опирается на методы структурного программирования и методы разработки программ, основанные на абстракции данных.
Структурное программирование связано с функциональной декомпозицией и предполагает проектирование программного продукта "сверху вниз". Однако такой метод не позволяет учесть зависимость архитектуры программы от структур данных, которые ей придется обрабатывать.
Использование подхода, основанного на абстракции данных ведет к противоположному эффекту: разработка программы осуществляется "от данных", а упор делается на выборе способа их представления. В этом случае, естественно, образуется разрыв между структурами данных и процедурами их обработки.
Объектно-ориентированное программирование позволяет ликвидировать противопоставление процедур данным и их неравноправность, свойственные двум указанным подходам, и одновременно с этим интегрирует достоинства рассмотренных методов разработки программ.
Таким образом, объектно-ориентированное программирование поддерживает качественно новый уровень совместной структуризации данных и процедур их обработки.
Как считает Алекс Лейн ("Мир ПК", 1991, 5, с.35), чтобы способ написания программ действительно стал другим, системы объектно-ориентированного программирования должны скорее вырастать из традиционных языков, чем вытеснять их. Обратная совместимость - и с ранее написанными программами, и с накопленным программистским опытом, - имеет решающее значение.
C++ - хороший пример такого эволюционного подхода. Кроме того, объектно-ориентированные коды должны генерироваться таким образом, чтобы позволить их последующую оптимизацию и предоставить программисту возможность контролировать максимальное число параметров. Такие особенности, как генерация машинного кода, допустимость традиционных методов и объектов, конструкторы и деструкторы - это не роскошь, а необходимость.
Ориентированное на объекты программирование имеет свое собственное множество понятий.
Основу ООП составляет понятие "объект", похожий на тип данных "запись" языка Pascal (или "структура" в языке C), но с одним существенным отличием: объекты включают в себя не только данные, но также процедуры и функции, называемые методами. Возможность объединения данных и правил в один тип позволяет разрабатывать программы на логическом уровне.
Объектный тип является структурой, состоящей из фиксированного числа компонент. Каждая компонента является либо полем, содержащим данные строго определенного типа, либо методом, выполняющим операции над объектом. По аналогии с объявлением переменных, объявление поля указывает тип данных этого поля и идентификатор, именующий поле: по аналогии с объявлением процедуры или функции, объявление метода указывает заголовок процедуры, функции, конструктора или сборщика мусора.
Кроме этого, объектно-ориентированное программирование, рассматриваемое нами как технология, базируется еще на трех основных понятиях: инкапсуляция, наследование и полиморфизм.
Основным методологическим положением ООП является то, что объекты моделируют характеристики и поведение элементов мира, в котором мы живем.
Как мы уже говорили, комбинирование записей с процедурами и функциями, манипулирующими полями этих записей, формирует новый тип данных - объект.
Как же обращаться к полям объекта? Как присваивать им значения? Поля данных объекта - это то, что объект знает, а методы объекта - это то, что объект делает. Для доступа к полям данных должны использоваться методы объекта.
Метод является процедурой или функцией, объявленной внутри объекта и жестко ограниченной этим объектом.
Одним из важнейших принципов объектно-ориентированного программирования является то, что программист во время разработки программы должен думать о коде и о данных совместно. Ни код, ни данные не существуют в вакууме. Данные управляют потоком кода, а код манипулирует образами и значениями данных. Если ваши код и данные являются разделенными сущностями, то всегда существует опасность вызова правильной процедуры с неверными данными или ошибочной процедуры с правильными данными. Забота о совпадении этих сущностей возлагается на программиста.
Объект осуществляет синхронизацию кода и данных путем совместного построения их объявлений. Реально, чтобы получить значение одного из полей объекта, вы вызываете относящийся к этому объекту метод, который возвращает значение нужного поля. Чтобы присвоить полю значение, вы вызываете метод, который назначает данному полю новое значение.
Некоторые ориентированные на объекты языки, например Smalltalk, требуют обязательного инкапсулирования, однако, например, в языке Turbo Pascal у вас есть выбор.
Не менее важным является и тот факт, что объекты могут наследовать характеристики и поведение того, что мы
называем порождающими объектами (или предками).
Этот процесс классификации называется таксономией. Это прекрасная начальная метафора для механизма наследования в объектно-ориентированном программировании.
Вопросами, которые задает ученый при попытке классификации некоторого животного или объекта, являются следующие:
Каждый конкретный класс имеет множество "поведений" и характеристик, определяющих этот класс. Ученый начинает с вершины конкретного генеалогического дерева и проходит по дочерним областям, задавая себе эти два вопроса. Наивысший уровень самый общий, а вопросы самые простые, например, крылатое или бескрылое? Каждый последующий уровень является более специфическим, чем предыдущий, и менее общим. В конце концов, ученый добирается до точки подсчета волосиков на третьем сегменте задней ноги насекомого!
Важно помнить то, что если характеристика однажды определена, то все категории, расположенные ниже данного определения, содержат эту характеристику. Таким образом, как только вы определили насекомое как члена отряда diptera (мухи), то вам не следует отмечать снова, что у мух имеется одна пара крыльев. Разновидность насекомых, которую мы зовем мухи, наследует эту характеристику от своего отряда.
Таким образом, мы отмечаем способность объектов наследовать данные и правила других объектов. Процесс, с помощью которого один тип наследует характеристики другого типа, называется наследованием.
Наследник называется порожденным (дочерним) типом, а тип, которому наследует дочерний тип, называется порождающим (родительским) типом. Наследование является транзитивным, т.е. если T3 наследует от T2, а T2 наследует от T1, то T3 наследует от T1. Владения объектного типа состоят из него самого и из всех его наследников.
Например, пусть имеется графический объект Circle. Это окружность заданного радиуса, выводимая определенным цветом в определенном месте экрана дисплея. При помощи механизма наследования можно создать объект FilledCircle (заполненная окружность), определив только один новый новый элемент - метод, по которому окружность будет закрашена нужным цветом.
Таким образом, наследование позволяет сократить размер кода до минимума и многократно использовать уже созданный код.
Если определен порожденный тип, то методы порождающего типа наследуются, однако при желании они могут быть подавлены. 0Например, для подавления наследуемого метода попросту объявите новый метод с тем же именем, что и наследуемый метод, но с другим телом и (при необходимости) с другим множеством параметров.
Как вы поняли, ориентированное на объекты программирование в большой степени является процессом
построения генеалогического дерева для структур данных. Одной из важных особенностей, которые
объектно-ориентированное программирование добавляет императивным языкам типа Pascal, является
механизм, с помощью которого типы данных могут наследовать характеристики более простых, более общих типов.
Полиморфизм - присваивание действию одного имени, которое затем разделяется "вниз" и "вверх" по иерархии объектов, причем каждый объект иерархии выполняет это действие способом, именно ему подходящим.
Иными словами, полиморфизмом является способность объектов следовать разным собственным правилам, имеющим одинаковые имена.
Объектно-ориентированное программирование фанатично "одушевляет" объекты в силу того, что случайности и закономерности, наполняющие нашу жизнь, имеют характеристики (данные) и линии поведения (методы).
Например, характеристики тостера могут включать требуемое напряжение, число гренок, которые он может поджарить одновременно, установку слабого или сильного уровней поджаривания, цвет тостера, его фабричную марку и т.д. Его поведение может включать загрузку кусков хлеба, поджаривание этих кусков и автоматическое выталкивание готовых гренок наружу.
Если мы хотим написать программу имитации кухни, то какой же имеется наилучший способ смоделировать различные приспособления, кроме объектов, с их характеристиками и линиями поведения, закодированными в поля данных и в методах? Фактически, это уже сделано: один из первых объектно-ориентированных языков (SIMULA-67) был создан как язык для написания таких имитаций.
Отныне данные для вас не емкости, которые вы можете наполнять значениями! С точки зрения нового взгляда на вещи, объекты выглядят как актеры на подмостках со множеством заученных ролей (методов). Если вы (дирижер) даете им слово, то актеры начинают декламировать в соответствиии со сценарием.
Например, в языке Turbo Pascal оператор APoint.MoveTo (242,118) можно "оживить", например, так: это распоряжение объекту APoint "Перенести себя в позицию 242,118."
Есть также причина того, что ООП довольно крепко связано в традиционном смысле с ориентированной на графикe средой. Объекты должны быть моделями, и есть ли лучший способ смоделировать объект, чем нарисовать его изображение?
Приведем основные вехи на пути развития объектно-ориентированного программирования [2, с.88-90].
Вторая половина 60-х годов. У.Дал с сотрудниками (Норвегия) разработал язык моделирования SIMULA-67, в котором были воплощены первоначальные идеи объектно-ориентированного программирования. Поскольку SIMULA-67 является языком моделирования, он требует описания объектов и их действий. Здесь уже имеются описания, называемые "класс" (class), и допускаются конструкции, в которых используется наследование объектов.
1983 год. Выпущен на рынок и приобрел широкую известность язык Smalltalk, разрабатывавшийся с начала 70-х годов Аланом Кеем и группой его коллег из Исследовательского центра фирмы Xerox в Пало-Альто (США), - первый объектно-ориентированный язык программирования. В нем реализованы механизмы вычислений посредством описания содержания объектов, описания действий, наследования и передачи сообщений.
1983 год. Группой исследователей во главе с Б.Страуструпом была окончена разработка языка программирования C++, представляющего собой результат дальнейшего развития языка программирования C в направлении объектной ориентированности и включения в него механизмов контроля типов, абстракции данных и совмещения операций. Название C++ выдумал Рик Масситти. Название указывает на эволюционную природу перехода к нему от C. "++" - это операция приращения в C. Б.Страуструп [8, с.10] шутит, что "знатоки семантики языка находят, что C++ хуже, чем ++C".
Вывод.
Объектно-ориентированное программирование должно стать в 1990-х годах тем же, чем было структурное
программирование в 1980-х - наиболее эффективным средством, позволяющим справиться со сложностью
программных комплексов.
На следующем шаге мы более подробно остановимся на императивной парадигме.