4.4. Объектно-ориентированное программирование
Объектно-ориентированное, или объектное программирование (в дальнейшем ООП) – парадигма программирования, в которой основными концепциями являются понятия объектов и классов. ООП возникло в результате развития идеологии процедурного программирования, где данные и подпрограммы (процедуры, функции) их обработки формально не связаны. Для дальнейшего развития ООП большое значение имеют понятия события (так называемое событийно-ориентированное программирование) и компонента (компонентное программирование, КОП).
Формирование КОП от ООП, как и модульного программирования, произошло от процедурного программирования: процедуры сформировались в модули (независимые части кода на уровне сборки программы), а объекты сформировались в компоненты (независимые части кода на уровне выполнения программы). Взаимодействие объектов происходит посредством сообщений. Результатом дальнейшего развития ООП, по-видимому, будет агентно-ориентированое программирование, где агенты — независимые части кода на уровне выполнения. Взаимодействие агентов происходит посредством изменения среды, в которой они находятся.
Первым языком ООП стала Симула (1967 г.). В нем были реализованы поистине революционные идеи: объекты, классы, виртуальные методы и др. Однако эти идеи не были восприняты современниками. Большинство концепций ООП были развиты А. Кейем и Д. Ингаллсом в языке Smalltalk. Он стал первым широко распространённым языком ООП. В настоящее время количество прикладных языков, реализующих парадигму ООП, является наибольшим по отношению к другим парадигмам. В области системного программирования до сих пор применяется процедурное программирование, которое ведется на языке C. Хотя и здесь заметно влияние языков ООП. Так одной из наиболее распространенных библиотек мультиплатформенного программирования является объектно-ориентированная библиотека Qt, разработанная на C++.
Основные понятия ООП
К основным понятиям ООП относятся: объект, класс, прототип, абстракция, инкапсуляция, наследование, полиморфизм.
Объект – сущность в адресном пространстве вычислительной системы, появляющаяся при создании экземпляра класса или копировании прототипа, например, после запуска результатов компиляции и связывания исходного кода для выполнения. Это центральное понятие ООП. Объект может посылать сообщения, принимать и реагировать на них, используя свои данные.
Класс является моделью ещё не существующей сущности (объекта), описываемой на языке терминологии исходного кода (пространство имён). Фактически класс описывает устройство объекта, является своего рода его чертежом. Говорят, что объект – это экземпляр класса. Обычно классы разрабатывают таким образом, чтобы их объекты соответствовали объектам предметной области.
Прототип – объект, по образу и подобию которого создаются другие объекты. Созданные объекты могут сохранять связь с родительским объектом, автоматически наследуя изменения в прототипе; эта особенность определяется в рамках конкретного языка. Прототип – альтернативное классам понятие (реализован в языке Self).
Абстрагирование – способ выделения значимых характеристик объекта из множества присущих ему. Соответственно абстракция – это набор выделенных характеристик.
Инкапсуляция – свойство системы, позволяющее объединить в классе данные и методы, работающие с ними, и скрывать детали их реализации от пользователя.
Наследование – свойство системы, позволяющее описать новый класс на основе уже существующего с частичной или полностью заимствованной функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс – потомком, наследником или производным классом.
Полиморфизм – свойство системы использовать объекты с одинаковым интерфейсом без знания типа и внутренней структуры объекта.
Наличие инкапсуляции, наследования и полиморфизма являются необходимыми и достаточными условиями объектной ориентированности языка программирования.
Определение ООП и его основные концепции
ООП насчитывает более чем сорокалетнюю историю, но до сих пор не существует общепринятого определения данной технологии. Принципы, заложенные в первые объектные языки и системы, подверглись существенному изменению и дополнению в ходе реализации последующие времена. С середины 1980-х годов термин «объектно-ориентированный» стал модным, в результате его стали «прикреплять» к любым новым разработкам, чтобы обеспечить их привлекательность.
Суть ООП. По мнению Кея (один из основателей ООП), объектно-ориентированный подход заключается в следующем. Всё является объектом. Вычисления осуществляются путём взаимодействия (обмена данными) объектов, в котором один объект требует от другого выполнения некоторого действия. Объекты взаимодействуют, посылая и получая сообщения. Сообщение – это запрос на выполнение действия, дополненный набором аргументов, которые могут понадобиться при его выполнении. Каждый объект имеет независимую память, которая состоит из других объектов. Каждый объект является представителем (экземпляром) класса, который выражает общие свойства объектов. В классе задаётся поведение (функциональность) объекта. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия. Классы организованы в единую древовидную структуру с общим корнем, называемую иерархией наследования. Память и поведение, связанное с экземплярами определённого класса, автоматически доступны любому классу, расположенному ниже в иерархическом дереве. Таким образом, программа представляет собой набор объектов, имеющих состояние и поведение. Объекты взаимодействуют посредством сообщений. Иерархия объектов выстраивается естественным образом. Программа в целом – объект, который для выполнения своих функций обращается к входящим в неё объектам, которые, в свою очередь, выполняют запрошенное действие путём обращения к другим объектам программы. Естественно, чтобы избежать бесконечной рекурсии в обращениях, на каком-то этапе объект трансформирует обращённое к нему сообщение в сообщения к стандартным системным объектам, предоставляемым средой программирования. Устойчивость и управляемость системы обеспечивается за счёт чёткого разделения ответственности объектов, однозначного определения интерфейсов межобъектного взаимодействия и полной изолированности внутренней структуры объекта от внешней среды.
Иерархия классов. Появление в ООП понятия класса обусловлено, необходимостью иметь множество объектов со сходным поведением. Класс в ООП – абстрактный тип данных, создаваемый программистом. Объекты являются значениями данного абстрактного типа, а определение класса задаёт внутреннюю структуру значений и набор операций, которые над ними могут выполняться. Необходимость иерархии классов (наследования) вытекает из требования повторного использования кода. Если классы имеют сходное поведение, нет смысла дублировать их описание, лучше выделить общую часть в общий родительский класс, а в описании самих классов оставить только различающиеся элементы.
Поддержка полиморфизма. Необходимость совместного использования объектов разных классов, способных обрабатывать однотипные сообщения, требует поддержки полиморфизма – возможности записывать разные объекты в переменные одного и того же типа. При этом объект, отправляя сообщение, может не знать, какому классу принадлежит адресат. Одни и те же сообщения, отправленные переменным одного типа, содержащим объекты разных классов, вызовут различную реакцию. Концепция обмена сообщениями реализуется посредством вызовов доступных извне методов. Данный подход использован в таких языках как C++, Object Pascal, Java, Oberon-2.
Наследование. Объект является значением, относящимся к определённому классу. Класс представляет собой составной тип данных, имеющий в составе: 1) поля данных – параметры, задающие состояние иначе свойства объекта; 2) методы – процедуры и функции, определяющие действия над объектом данного типа, которые объект может выполнять. В силу свойства наследования класс-потомок получает все свойства и методы класса-родителя, но может дополнить их собственными свойствами, или переопределить имеющиеся. В основном поддерживается только единичное наследование. Класс может иметь только одного родителя. Множественное наследование – порождение класса от двух и более родителей из-за сложности реализации практически не используется. Вместо него используется понятие интерфейса. Интерфейс – это класс без полей и без реализации, включающий только заголовки методов. Если некий класс наследует интерфейс, он должен реализовать все входящие в него методы. Использование интерфейсов – относительно дешёвая альтернатива множественному наследованию.
Инкапсуляция. Обеспечивается посредством контроля и методов доступа. Методы класса могут быть как чисто внутренними, обеспечивающими логику функционирования объекта, так и внешними, с помощью которых взаимодействуют объекты, необходимо обеспечить скрытость первых при доступности извне вторых. Для этого в языки вводятся специальные синтаксические конструкции, явно задающие область видимости каждого члена класса. Для этого используются модификаторы типа public (открытые члены класса), protected (доступные только из классов-потомков) и private (доступные только внутри класса). Для защиты внутреннего состояния объекта поля его класса не должны быть доступны извне. Поэтому поля объявляются скрытыми, а для доступа к их данным используются специальные методы доступа. Такие методы либо возвращают значение поля, либо производят запись нового значения в него. При записи метод контролирует допустимость значения и его корректность (внутреннюю согласованность). Методы доступа в общем называют аксессорами, а в частности – геттерами (методы чтения) и сеттерами (методы записи).
Свойства объекта. Это псевдополя, доступные для чтения и/или записи. Свойства внешне выглядят как поля и используются аналогично доступным полям, однако при обращении к ним происходит вызов методов доступа. Таким образом, свойства можно рассматривать как поля данных, сопровождающие доступ к внутренним данным объекта какими-либо дополнительными действиями например, когда изменение координаты объекта сопровождается его перерисовкой на новом месте. Свойства, по сути лишь скрывают вызов методов доступа. В C# объявление свойства содержит код методов доступа, который вызывается только при работе с этим свойством. В Delphi объявление свойства содержит только имена методов доступа, которые будут вызваны при обращении к полю.
Подходы к проектированию программ в целом
ООП ориентировано на разработку крупных программных комплексов, разрабатываемых командой программистов. Проектирование системы в целом, создание отдельных компонент и их объединение в конечный продукт выполняется разными людьми, и поэтому эффективное решение задачи требует подходов, уже изложенных ранее. Объектно-ориентированное проектирование состоит в описании структуры и поведения проектируемой системы, то есть, в ответе на два вопроса: 1) из каких частей состоит система, 2) в чём ответственность каждой части. Выделение частей производится по принципу, каждая часть должна иметь минимальный объём и точно определённый набор выполняемых функций, и при этом взаимодействовала с другими частями как можно меньше. Дальнейшее уточнение приводит к выделению более мелких фрагментов описания. По мере детализации описания и определения ответственности выявляются данные, которые необходимо хранить, а также близкие по поведению агенты, которые становятся кандидатами на реализацию в виде классов. После выделения компонентов и определения интерфейсов между ними реализация каждого компонента может проводиться практически независимо от остальных.
Определяющее значение имеет построение иерархии классов. Одна из главных проблем построения больших систем на основе ООП ‑ определенность базового класса. Она состоит в том, что на поздних этапах разработки, когда иерархия классов выстроена и на её основе разработан код программы, сложно или даже невозможно внести какие-либо изменения в базовые классы иерархии при модификации пролграммы. Даже если эти изменения не затронут интерфейс базового класса, изменение его поведения может непредсказуемым образом отразиться на классах-потомках. В случае большой системы разработчик базового класса не в состоянии предугадать последствия изменений, их влияние на корректность работы классов-потомков.
Родственные методологии
Компонентно-ориентированное программирование (КОП) ‑ следующий этап развития ООП. Это своеобразная надстройка над ООП, набор правил и ограничений, направленных на построение крупных развивающихся программных систем с большим временем жизни. Программная система в этой методологии представляет собой набор компонентов с хорошо определёнными интерфейсами. Изменения в существующую систему вносятся путём создания новых компонентов в дополнение или в качестве замены ранее существующих. При создании новых компонентов на основе ранее созданных запрещено использование наследования реализации ‑ новый компонент может наследовать лишь интерфейсы базового. Таким образом, КОП обходит проблему определенности базового класса.
Прототипное программирование, сохранив часть черт ООП, отказалось от базовых понятий класса и наследования. Вместо механизма описания классов и порождения экземпляров предоставляется механизм создания объекта путём задания набора полей и методов, которыми объект должен обладать, и механизм клонирования объекта. Каждый вновь созданный объект является экземпляром, но без класса. Каждый объект может стать прототипом ‑ быть использован для создания нового объекта с помощью операции клонирования. После клонирования новый объект можно изменять, в частности, дополнять новыми полями и методами. Клонированный объект либо становится полной копией прототипа, хранящей все значения его полей и дублирующей его методы, либо сохраняет ссылку на прототип, не включая в себя клонированных полей и методов до тех пор, пока они не будут изменены. В этом случае среда исполнения обеспечивает механизм делегирования. Если при обращении к объекту он сам не содержит нужного метода или поля данных, вызов передаётся прототипу, а от него, при необходимости, дальше по цепочке.
Производительность объектных программ
Буч указывает на следующие причины, приводящие к снижению производительности программ, разработанных на основе ООП.
1) Динамическое связывание методов. Обеспечение полиморфного поведения объектов приводит к необходимости связывать методы, вызываемые программой, не на этапе компиляции, а в процессе исполнения программы, на что тратится дополнительное время. При этом связывание требуется не более чем для 20% вызовов.
2) Глубина абстракции. ООП приводит к созданию многослойных приложений, где выполнение объектом требуемого действия сводится к множеству обращений к объектам более низкого уровня. В таком приложении методы часто вызываются, что снижает производительность.
3) Размытие кода. Код, используемый программой непосредственно и относящийся к оконечным классам иерархии наследования, находится не только в этих классах, но и в их классах-предках. Это приводит к снижению скорости трансляции программ (компоновщик подгружает описания всех классов иерархии) и их производительности в системах со страничной памятью (методы одного класса находятся на разных страницах памяти, активно обращающихся к унаследованным методам, в результате ‑ частое переключение страниц).
3) Издержки инкапсуляции. Запрет на прямой доступ к полям класса извне приводит к необходимости использования методов доступа, что сопряжено с дополнительными расходами.
4) Динамическое создание и уничтожение объектов. Создаваемые объекты размещаются в “куче”, что менее эффективно, чем размещение их в стеке, а тем более, в статической памяти.
Несмотря на отмеченные недостатки, использование ООП в отдельных случаях дает более весомые результаты. Повышение производительности за счёт лучшей организации ООП-кода иногда компенсирует дополнительные расходы на организацию функционирования программы. В настоящее время ООП используется в большинстве промышленных проектов. Однако считать ООП наилучшей методикой программирования не следует. Сравнивая объектное и процедурное программирование, следует отметить. что процедурное лучше тогда, когда важны быстродействие и используемые ресурсы, но требуется больше времени для разработки, а объектное ‑ когда важны управляемость проекта, его модифицируемость и скорость разработки.
- Введение в программирование и основы алгоритмизации
- 1.2. Понятие "правильной" программы
- 1.3. Надежность программного средства
- 1.4. Технология программирования как разработка надежных пс
- 1.5. Информатизация общества
- Тема 2 источники ошибок в программных средствах
- 2.1. Интеллектуальные возможности человека
- 2.2. Неправильный перевод как причина ошибок в пс
- 2.3. Модель перевода
- На каждом из этих шагов человек может совершить ошибку разной природы.
- 2.4. Основные пути борьбы с ошибками
- Тема 3 общие принципы разработки программных средств
- 3.1. Специфика разработки пс
- 3.2. Жизненный цикл пс
- 3.3. Понятие качества пс
- 3.4. Внешнего описания и его роль в обеспечении качества пс
- 3.5. Обеспечение надежности – основной мотив разработки пс
- 3.5. Борьба со сложностью систем и обеспечение точности перевода
- Тема 4 разработка структуры программы. Модульное и объектно-ориентированное программирование
- 4.1. Цель модульного программирования
- 4.2. Основные характеристики программного модуля
- 4.3. Методы разработки структуры программы
- 4.4. Объектно-ориентированное программирование
- 4.5. События и событийная модель
- Тема 5 Алгоритмизация и разработка программного модуля
- 5.1. Определение алгоритма
- Алгоритмизация - техника составления алгоритмов и программ для решения задач на эвм.
- 5.2. Изобразительные средства описания алгоритмов
- 5.3. Блок-схемы алгоритмов. Графические символы
- 5.4. Порядок разработки программного модуля
- 5.5. Структурное программирование
- 5.6. Пошаговая детализация и понятие о псевдокоде
- Тема 6 тестирование и отладка программного средства
- 6.1. Основные понятия
- 6.2. Принципы и виды отладки пс
- 6.3. Заповеди отладки пс
- 6.4. Автономная отладка пс
- Тема 7 Методы разработки алгоритмов
- 7.1. Метод частных целей
- 7.2. Метод подъема
- 7.3. Программирование с отходом назад
- Тема 8 Алгоритмы сортировки
- 8.1. Сортировка. Основные понятия
- 8.2. Пузырьковая сортировка
- 8.3. Сортировка с помощью дерева
- 8.4. Пирамидальная сортировка
- 8.5. Быстрая сортировка
- Тема 9 Алгоритмы поиска и перебора
- 9.1. Поиск. Основные понятия
- 9.2. Бинарный поиск
- 9.3. Поиск в сети
- Тема 10 Событийно-управляемое программирование на языке Visual Basic
- 10.1. Историческая справка
- 10.2. Основы Visual Basic
- Среда Windows: окна, события, сообщения
- Интерактивная разработка
- Интегрированная среда разработки
- 10.3. Формы и элементы управления
- Разработка и установка свойств формы
- События и методы формы
- Кнопки управления как основа выполнения действий
- 10.4. Элементы управления пользователя
- Флажки и переключатели
- Другие стандартные элементы управления
- 10.5. Фокус. Последовательность переходов. Меню Фокус
- Основы меню
- Контекстные меню
- Редактор меню
- Подсказки пользователю с помощью диалога
- Тема 11 Управление проектами
- 11.1. Работа с проектом и его структура
- 11.2. Работа с несколькими проектами
- 11.4. Установка параметров проекта
- 11.5. Дополнения и мастера
- Тема 12 Управляющие конструкции
- 12.1. Конструкции принятия решения (ветвление)
- 12.2. Циклы
- 12.3. Работа со структурами управления и досрочный выход из них
- Тема 13 Структура приложения. Техника написания кода
- 13.1. Структура приложения
- 13.2. Как работает событийное приложение
- 13.3. До начала кодирования
- 13.4. Техника написания кода
- 13.5. Автоматизация написания программы