Объектно-ориентированное программирование. Принципы объектно-ориентированного программирования

Объектно-ориентированное программирование (ООП) позволяет разложить проблему на составные части, каждая из которых становится самостоятельным объектом. Каждый из объектов содержит свой собственный код и данные, которые относятся к этому объекту.

Любая программа, написанная на языке ООП, отражает в своих данных состояние физических предметов либо абстрактных понятий – объектов программирования, для работы, с которыми она предназначена.

Все данные об объекте программирования и его связях с другими объектами можно объединить в одну структурированную переменную. В первом приближении ее можно назвать объектом.

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

Описание множества однотипных объектов называется классом .
Объект – это структурированная переменная, содержащая всю информацию о некотором физическом предмете или реализуемом в программе понятии.

Класс – это описание множества объектов программирования (объектов) и выполняемых над ними действий.

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

Основные понятия объектно-ориентированного программирования

Любая функция в программе представляет собой метод для объекта некоторого класса.

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

Вся программа в таком виде представляет собой объект некоторого класса с единственным методом run (выполнить).

Программирование «от класса к классу» включает в себя ряд новых понятий. Основными понятиями ООП являются

  • инкапсуляция;
  • наследование;
  • полиморфизм.

Инкапсуляция данных (от «капсула») – это механизм, который объединяет данные и код, манипулирующий с этими данными, а также защищает и то, и другое от внешнего вмешательства или неправильного использования. В ООП код и данные могут быть объединены вместе (в так называемый «черный ящик») при создании объекта.

Внутри объекта коды и данные могут быть закрытыми или открытыми.

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

Открытые коды и данные, напротив, доступны для всех частей программы, в том числе и для других частей того же самого объекта.

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

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

Иначе говоря, новый класс наследует как данные старого класса, так и методы их обработки.

Если объект наследует свои свойства от одного родителя, то говорят об одиночном наследовании . Если объект наследует данные и методы от нескольких базовых классов, то говорят о множественном наследовании .

Пример наследования – определение структуры, отдельный член которой является ранее определенной структурой.

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

Целью полиморфизма, применительно к ООП, является использование одного имени для задания действий, общих для ряда классов объектов. Такой полиморфизм основывается на возможности включения в данные объекта также и информации о методах их обработки (в виде указателей на функции).

Будучи доступным в некоторой точке программы, объект, даже при отсутствии полной информации о его типе, всегда может корректно вызвать свойственные ему методы.
Полиморфная функция – это семейство функций с одним и тем же именем, но выполняющие различные действия в зависимости от условий вызова.

Например, нахождение абсолютной величины в языке Си требует трех разных функций с разными именами.

Всем привет.

Неделя статей на хабре посвященная ООП. Последняя статья вызвала у меня кучу эмоций и, к сожалению, очень плохих эмоций. Мне очень не понравилась статья. Почему? Потому что в ней передаются какие-то отрицательные эмоции об использовании ООП. Эмоции вызваны лишь тем, что человек не до конца понимает всю силу ООП и хочет убедить всех в том что ООП зло. Самое печальное что люди начинают прислушиваться и кидаться ужасными доводами, не имеющими ничего общего с действительностью. Я думаю что студентам такие статьи противопоказаны больше чем GoF, которых я бы давал как можно раньше. :)

Начнем.

Что такое ООП. ООП - это и ОО программирование и проектирование. Одно без другого бессмысленно чуть более чем полностью. Создано ООП для проектирования/программирования программных продуктов. Не для моделирования процессов. Не для проектирования протоколов, а именно для программных продуктов, для их реализации. Для упрощения системы, которая будет реализовывать протокол или бизнес-процесс или что-то еще.

Когда вы начинаете использовать ООП, первое что вы должны сделать - это начать использовать объектное мышление. Я уже когда-то говорил что это самая большая проблема ООП, научиться мыслить объектно очень сложно. И очень важно учиться это делать как можно раньше (GoF с аналогиями типа мост, конструктор, фасад очень в этом помогут). Используя объектное мышление, вы легко сможете проектировать сложные системы Используя объектное мышление вы легко можете решить любую задачу (очень важно что любую задачу проектирования/программирования, если ее в принципе можно решить абсолютно любую) оперируя объектами и взаимодействием между ними. Т.е. ООП без объектного мышления не позволит вам начать использовать всю силу и мощь ООП.

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

Как же эти инструменты работают? Да проще пареной репы, потому что это все основано на привычных нам вещах. Люблю простые примеры из жизни:

1. Наследование. Есть пекарь. Есть печь электрическая и газовая. Ваша задача смоделировать процесс приготовления пищи пекарем в каждой из печи. Решая задачу в лоб, у нас будет много дублирования кода из-за того, что сам процесс передачи пищи в печь и сама работа с печами идентичны для обеих печей. Но если мы включаем объектное мышление, и вспоминаем про инструмент наследование, то получаем примерно следующее (диаграмму лень рисовать, сорри):
Есть печь (абстрактная печь). У нее есть поведение - включить, выключить, увеличить или уменьшить температуру, положить чего-то, достать чего-то и состояние - температура в печи, включена или выключена. Это отличный пример абстрактного объекта в котором соблюдены принципы инкапсуляции (при реализации я их обязательно буду соблюдать). И есть пекарь, конкретный такой пекарь Иван. Он умеет работать с абстрактной печью. Т.е. смотреть температуру, включать выключать и т.д. вы поняли. Сила наследования в том, что нам не придется переписывать нашего Ивана для каждой из печей, будь то электро или газовая печь. Я думаю всем ясно почему? Получается что инструмент применен правильно.
2. Полиморфизм. Печи ведь по-разному работают. Газовая потребляет газ, электро печь - электричество. Используя полиморфизм мы легко меняем поведение в наследниках абстрактной печи.
3. Инкапсуляция. Основная фишка инкапсуляции в том, что я не должен знать, что происходит внутри моей печи. Допустим, я вызываю не метод включить печь, а меняю ее свойство включена на значение true. Что произойдет в этот момент? Если принцип инкапсуляции не соблюден, то я буду вынужден печи сказать начинай потреблять горючее, т.к. я тебя включил. Т.е. пекарь знает, что печь потребляет горючее, знает, как печь работает. Или, например, мы не можем установить температуру печи ниже или выше определенного уровня. Если не соблюдать принцип инкапсуляции, то мы должны будем говорить печи проверь-ка текущую температуру, пойдет те такая? Т.е. пекарь опять слишком много знает о печи. Геттеры и сеттеры это средства языка, которые помогут нам легко реализовать отслеживание изменений состояния. Все. Если геттеры и сеттеры пустые, значит так надо на моем уровне абстракции. Геттеры и сеттеры - не могут мешать реализации инкапсуляции, криво реализовать инкапсуляцию может проектировщик/программист.

В данном примере уровень абстракции выбран хорошо. Все занимаются своими делами, все три кита ООП работают во славу. Но стоит мне выбрать плохие абстракции, как начинается сущий кошмар. И даже есть стандарты чеклисты, которые помогут понять, хорошо ли вы выбрали абстракции и верна ли ваша декомпозиция в том ли направлении вы идете (SOLID).

Еще стали добавлять абстракцию, как еще один столп ООП. Я думаю, что это скорее верно, но уж очень попахивает КЭПом.

Высказывания про типизацию меня тоже зацепили. Дело в том, что никаких проблем в том, с кем вы сейчас работаете из наследников нет. Если на текущем уровне абстракции вам важно именно использовать печь, то вам не важно какая она. Вы получаете печь? Вы решаете свои задачи? То то и оно… Почему вы считаете что это динамическая типизация мне не понятно. Вы хотели печь? Берите. Вам нужна электрическая? Ну извините, газовая вам уже не подойдет.

Остальные примеры, которые были приведены в зацепившей меня статье, лишь примеры отвратительно выбранной абстракции и аналогии в рамках поставленной задачи. Точка.

Отдельно про DTO. DTO - это паттерн. Он позволяет создать объект, который передаст информацию другому слою, другой системе, короче куда-то чего-то передаст. Почему он не может быть рассмотрен мною как объект для меня вообще загадка. Где противоречие то? Является контейнером только? Ну и что?? Это же объект в рамках рассмотренной мною объектной модели на заданном уровне абстракции, где DTO - объект и часть декомпозиции.

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

Еще говорят что некоторые вещи нельзя представить в виде объектов и их взаимодействия. Я уверен что это не так. Просто необходимо выбрать уровень абстракции верно. Будь то реализация протокола, слоя доступа к БД, подключения плагинов, менеджера задач, бизнес процесса, системы проектирования бизнес процессов т.е. все что угодно можно представить как объекты и их взаимодействие. Все можно реализовать как объекты и взаимодействие между ними. Хорошо это или плохо чаще всего зависит лишь от вашего умения мыслить объектно.

Резюмируя. Если вы не понимаете силу ООП, то скорее всего вам надо развивать объектное мышление.

P.S. В комментах к прошлой статье я явно много перегибал палку при обращении к некоторым людям. Приношу свои извинения.

Подборка видео по теме объектно-ориентированного программирования. Годится для новичков, изучающих ООП, и для подготовки к .

1. Введение в обучающий видеокурс по основам объектно-ориентированного программирования (ООП)

В этом вступительном ролике задается вектор всего курса. Здесь описывается план занятий, а автор делает акцент на том, что видео выстроены в логической последовательности. Рекомендуется смотреть ролики строго друг за другом, не перескакивая на другие темы, чтобы не запутаться.

2. Основные принципы объектно-ориентированного программирования. Что такое ООП и зачем оно нужно?

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

3. Понятие класса и объекта в ООП

В этом видео будет затронута самая важная тема курса – классы и объекты. По словам автора курса, полное понимание этих тем обеспечивает понимание объектно-ориентированного программирование на 70 процентов. Подробно рассмотрим процесс проектирования и воспроизводства классов и их объектов.

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

5. Методы и данные в ООП

Что находится внутри класса, из чего он состоит? Что такое данные и методы, и без чего можно обойтись в описании класса? Как описывать метод, передавать параметры? Чем отличается метод класса в ООП от функции в процедурном программировании? В данном видео есть ответы на все перечисленные вопросы.

6. Методы в ООП. Типы методов

Продолжаем изучать тему методов в объектно-ориентированном программировании. Какие виды методов существует и зачем они нужны? Чем методы экземпляров отличаются от методов класса? Конструктор, деструктор, getter, setter – что это такое и зачем эти методы нужны?

7. Каким должен быть хороший метод в ООП

Теперь, зная основы создания методов в ООП, можно заняться вопросом создания хорошего метода. Здесь все так же, как с классом: если вы поняли суть методов и их роль в объектно-ориентированном программировании, вы почти наверняка будете описывать методы правильно. Но что бывает, если метод выглядит не так, как нужно, и что с этим делать – выясняем в этом видео.

8. Наследование в ООП

Поговорим об одном из самых мощных инструментов ООП – наследовании, и о том, как использовать этот механизм с умом. На примере рассмотрим процесс построения родительского класса и создание дочерних классов на его основе.

9. Инкапсуляция данных в ООП

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

10. Абстракция в ООП

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

11. Полиморфизм в ООП

Что нужно знать, прежде чем понять эту парадигму? Что означает «полиморфизм»? Развиваем идею абстрагирования классов и методов, а также рассмотрим пример применения полиморфизма.

Объектно-ориентированное программирование представляет собой дальнейшее развитие идей структурного программирования, основной целью которого является создание программ простой структуры. Это достигается за счет разбиения программы на максимально обособленные части.

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

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

Объект, или класс, является абстрактным типом данных, создаваемым программистом. При описании класса определяются его поля (данные) я методы (подпрограммы их обработки). Конкретные переменные объектного типа называются экземплярами объекта. Аналогия из обыденной жизни: собака является объектом, а экземплярами - конкретные Жучка или Терри.

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

К преимуществам ООП относятся:

  • ? использование при программировании понятий, более близких к предметной области;
  • ? более простая структура программы в результате инкапсуляции, т.е. объединения свойств и поведения объекта и скрытия деталей его реализации;
  • ? возможность повторного использования кода за счет наследования;
  • ? сравнительно простая возможность модификации программы;
  • ? возможность создания библиотек объектов.

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

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

Объекты В ПАСКАЛЕ объект - это тип данных,

Определяемый программистом. Объект

похож на тип «запись» (record), но кроме полей данных в нем можно описывать методы. Методами называются подпрограммы, предназначенные для работы с полями объекта. Внутри объекта описываются только заголовки методов:

type имя = object

описание элементов

заголовки методов

Поля и методы называются элементами объекта. Их видимостью управляют директивы private и public 1 . Ключевое слово private (закрытые ) ограничивает видимость перечисленных после него элементов файлом, в котором описан объект. Действие директивы распространяется до другой директивы или до конца объекта. В объекте может быть произвольное количество разделов private и public. По умолчанию все элементы объекта считаются видимыми извне, т.е. являются public (открытыми).

Для каждого поля объекта задается его имя и тип. Он может быть любым, кроме типа того же объекта, но может быть указателем на этот тип.

При определении состава методов исходят из требуемого поведения объекта. Каждое действие, которое должен выполнять объект, оформляется в виде отдельной процедуры или функции.

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

Квадратные скобки означают, что эти директивы являются необязательными.

определенными «запасами» здоровья и вооружения и цветом, а также умеет атаковать противника: type monster = object

procedure init (x_, y_, health_, ammo_: word);

procedure attack;

procedure erase;

procedure move (x_, y_: word); private

health, ammo: word; color: word;

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

Описание методов (текст подпрограмм) размещается вне объекта в разделе описания процедур и функций:

procedure monster.init (х_, у_, health_, ammoj word); begin x:= x_; y:= y_; health:= health_; ammo:= ammo_; color:= yellow; end; procedure monster.draw; begin ... {процедуры вывода изображения} end;

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

private, а методы - public.

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

Экземпляры объектов Экземпляр объекта - это переменная

Объектного типа. Время жизни и видимость объектов зависят от вида и места их описания и подчиняются общим правилам ПАСКАЛЯ. Экземпляры объектов можно создавать в статической или динамической памяти:

var Vasia: monster; {описывается статический объект} pm: "?"monster; {описывается указатель на объект}

new (pm); {создается динамический объект}

Можно определять массивы объектов или указателей на объекты и создавать из них динамические структуры данных. Если объектный тип описан в модуле, для создания в программе переменных этого типа следует подключить модуль в разделе uses: uses graph, monsters;

Доступ к элементам объекта осуществляется либо с использованием составного имени, либо с помощью оператора with: Vasia.erase;

with pm A do begin init (100, 100, 30); draw; end;

Если объект описан в модуле, то получить доступ к значениям полей со спецификатором private в программе можно только через обращение к соответствующим методам.

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

Объекты одного типа можно присваивать друг другу, при этом выполняется поэлементное копирование всех полей. Кроме того, в ПАСКАЛЕ определены правила расширенной совместимости типов объектов. Они рассмотрены далее. Все остальные действия выполняются над отдельными полями объектов.

Управлять большим количеством Иерархии объектов

разрозненных объектов сложно. С этим -

можно справиться путем упорядочивания и ранжирования объектов, т.е. объединяя общие для нескольких объектов свойства в одном объекте и используя этот объект в качестве базового.

Эту возможность предоставляет механизм наследования. Он позволяет строить иерархии, в которых объекты-потомки получают свойства объектов-предков и могут дополнять их или изменять. Таким образом, наследование обеспечивает возможность повторного использования кода.

При описании объекта имя его предка записывается в круглых скобках после ключевого слова object.

Пусть требуется ввести в игру еще один тип персонажей, который должен обладать свойствами объекта monster, но по-другому выглядеть и атаковать:

type daemon = object (monster)

procedure init (x_, y_, health_, ammo_, magic_: word);

procedure attack; procedure draw; procedure erase; procedure wizardry; private

magic: word; end;

{---реализация методов объекта daemon---->

procedure daemon.init (x_, y_, health_, ammo_, magic_: word); begin

inherited init (x_, y_, health_, ammoj; color:= green; magic:= magic_; end;

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

Наследование методов. В «потомке» объекта можно не только описывать новые методы, но и переопределять существующие. Метод можно переопределить либо полностью, либо дополнив метод «предка».

Последний вариант иллюстрируется с помощью метода in it. В объекте daemon этот метод переопределен. Из него с помощью ключевого слова inherited (унаследованный) сначала вызывается старый метод, а затем выполняются дополнительные действия. Можно вызвать метод «предка» и явным образом с помощью конструкции monster, init.

Совместимость типов ПАСКАЛЬ - язык со строгой типизацией,

объектов Операнды выражений, параметры под-

программ и их аргументы должны подчиняться правилам соответствия типов. Для объектов понятие совместимости расширено: производный тип совместим со своим родительским типом. Эта расширенная совместимость типов имеет три формы:

  • 1) между экземплярами объектов;
  • 2) между указателями на экземпляры объектов;
  • 3) между параметрами и аргументами подпрограмм.

Во всех трех случаях родительскому объекту может быть присвоен экземпляр любого из его потомков, но не наоборот (рис. 6.6). Например, если определены переменные: type

pmonster = A monster; pdaemon = ^daemon; var m: monster; d: daemon; pm: pmonster; pd: pdaemon;

Рис. 6.6.

Поля и методы, введенные в потомке, после таких присваиваний недоступны. Даже если метод переопределен в потомке, через указатель на предка вызывается метод, описанный в предке. Так, в результате выполнения оператора pm A .draw на экране появится изображение объекта-предка, потому что тип вызываемого метода соответствует типу указателя, а не типу того объекта, на который он ссылается.

Если известно, что указатель на предка на самом деле хранит ссылку на потомка, можно обратиться к элементам, определенным в потомке, с помощью явного преобразования типа, например pdaemon (pm) A .wizardry.

Если объект является параметром подпрограммы, ему может соответствовать аргумент того же типа или типа любого из его потомков.

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

Рассмотрим работу компилятора при Виртуальные методы

использовании в программе иерархий -

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

Ясно, что с помощью этого механизма не удастся обеспечить возможность вызова методов разных объектов (предков и потомков) с помощью одного и того же указателя или из одной и той же подпрограммы. Это можно сделать только в том случае, если ссылки будут разрешаться на этапе выполнения программы в момент вызова метода. Такой механизм называется поздним связыванием и реализуется с помощью виртуальных методов. Они определяются с помощью директивы virtual, которая записывается в заголовке метода:

procedure attack; virtual;

Объявление метода виртуальным означает, что все ссылки на этот метод будут разрешаться по факту его вызова, т.е. не на стадии компиляции, а во время выполнения программы. Для реализации этой возможности необходимо, чтобы адреса виртуальных методов хранились там, где ими можно в любой момент воспользоваться, поэтому компилятор формирует для них таблицу виртуальных методов (Virtual Method Table-VMT). Для каждого объектного типа создается одна VMT.

Каждый объект во время выполнения программы должен иметь доступ к VMT. Эта связь устанавливается при создании объекта с помощью специального метода, называемого конструктором. Класс, имеющий хотя бы один виртуальный метод, должен содержать конструктор, например:

type monster = object

constructor init (x_, y_, health_, ammo_: word);

procedure attack; virtual;

procedure draw; virtual;

procedure erase; virtual;

procedure move (x_, y_: word);

x, у: word; health, ammo: word; color: word;

daemon = object (monster)

constructor init (x_, y_, health_, ammo_, magic_: word); procedure attack; virtual;

procedure draw; virtual; procedure erase; virtual; procedure wizardry; private

magic: word; end;

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

Конструктор обычно используется для инициализации объекта.

В нем выполняется выделение памяти под динамические переменные или структуры, если они есть в объекте, и присваиваются начальные значения полям объекта. Поскольку связь с VMT устанавливается в самом начале конструктора, в его теле также можно пользоваться виртуальными методами.

Объект может содержать несколько конструкторов. Конструктор надо вызывать явным образом для каждого создаваемого объекта.

Вызов виртуального метода выполняется так: из объекта берется адрес его VMT, из VMT выбирается адрес метода, а затем управление передается этому методу (рис. 6.7). Таким образом, при использовании виртуальных методов из всех одноименных методов иерархии всегда выбирается тот, который соответствует фактическому типу вызвавшего его объекта.

Правила описания виртуальных методов.

  • 1. Если в объекте метод определен как виртуальный, во всех потомках он также должен быть виртуальным.
  • 2. Заголовки всех одноименных виртуальных методов должны совпадать.
  • 3. Переопределять виртуальный метод в каждом из потомков не обязательно: если он выполняет устраивающие потомка действия, он будет унаследован.
  • 4. Объект, имеющий хотя бы один виртуальный метод, должен содержать конструктор.
  • 5. При описании объектов рекомендуется определять как виртуальные те методы, которые в его потомках будут реализовываться по-другому.

Рис. 6.7.

Объекты в динамической Для хранения объектов в программах чаще памяти всего используется динамическая память,

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

Для выделения памяти под объекты используются процедура и функция new. Например, если определены указатели:

pmonster = A monster; pdaemon = A daemon;

var pm: pmonster; pd: pdaemon;

можно создать объекты с помощью вызовов: new (pm); {или pm:= new (pmonster);} new (pd); {или pd:= new (pdaemon);}

Так как после выделения памяти объект обычно инициализируют, для удобства определены расширенные формы new со вторым параметром - конструктором объекта: new (pm, init (1, 1, 1, 1);

{или pm:= new (pmonster, init (1, 1, 1, 1));} new (pd, init (1, 1, 1, 1, 1);

{или pd:= new (pdaemon, init (1, 1, 1, 1, 1));} Обращение к методам динамического объекта выполняется по обычным правилам:

pm^.draw; pmA.attack;

С объектами в динамической памяти часто работают через указатели на базовый класс, т.е. описывают указатель базового класса, а инициализируют его, создав объект производного класса: pm: = new (pdaemon, init (1, 1, 1, 1, 1));

Такие объекты называют полиморфными. Они используются, для того чтобы можно было работать с объектами разных классов единообразно.

Деструкторы

Для освобождения памяти, занятой объектом, применяется процедура

Она освобождает количество байтов, равное размеру объекта, соответствующего типу указателя. Если в указателе хранится ссылка на объект производного класса, будет освобождено неверное количество байтов. Для корректного освобождения памяти из-под полиморфных объектов следует использовать вторым параметром Dispose специальный метод - деструктор. В документации по Borland Pascal ему рекомендуется давать имя done:

destructor monster.done; begin end; Dispose (pm, done);

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

Компилятор по служебному слову destructor вставляет в конец тела метода операторы получения размера объекта из VMT. Деструктор передает этот размер процедуре Dispose, и она освобождает количество памяти, соответствующее фактическому типу объекта. Деструкторы обязательно использовать только для динамических полиморфных объектов. В объекте можно определить несколько деструкторов (в этом случае они должны иметь разные имена).

Основные принципы и этапы объектно-ориентированного

программирования

В теории программирования ООП определяется как технология создания сложного программного обеспечения, которая основана на представлении программы в виде совокупности объектов , каждый из которых является экземпляром определенноготипа (класса ), а классы образуют иерархию с

наследованием свойств .

Взаимодействие программных объектов в такой системе осуществляется путем передачи сообщений .

П р и м е ч а н и е. Такое представление программы впервые было использовано в языке имитационного моделирования сложных систем Simula, появившемся еще в 60-х годах.

Естественный для языков моделирования способ представления программы получил развитие в другом специализированном языке моделирования - языке Smalltalk (70-е годы), а затем был

Страница 2 из51

Основные принципы ООП

использован в новых версиях универсальных языков программирования, таких как Pascal, С++,

Основное достоинство ООП - сокращение количества межмодульных вызовов и уменьшение объемов информации, передаваемой между модулями,

по сравнению с модульным программированием. Это достигается за счет более полной локализации данных и интегрирования их с подпрограммами обработки,

что позволяет вести практически независимую разработку отдельных частей

(объектов) программы.

Кроме этого, объектный подход предлагает новые технологические средства разработки, такие как наследование, полиморфизм, композиция, наполнение ,

позволяющие конструировать сложные объекты из более простых. В результате существенно увеличивается показатель повторного использования кодов,

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

Основной недостаток ООП - некоторое снижение быстродействия за счет более сложной организации программной системы.

В основу ООП положены следующие п р и н ц и п ы : абстрагирование,

ограничение доступа, модульность, иерархичность, типизация, параллелизм,

устойчивость.

Рассмотрим, что представляет собой каждый принцип.

А б с т р а г и р о в а н и е - процесс выделения абстракций в предметной области задачи.Абстракция - совокупность существенных характеристик некоторого объекта, которые отличают его от всех других видов объектов и,

таким образом, четко определяют особенности данного объекта с точки зрения дальнейшего рассмотрения и анализа. В соответствии с определением применяемая абстракция реального предмета существенно зависит от решаемой задачи: в одном случае нас будет интересовать форма предмета, в другом вес, в

третьем - материалы, из которых он сделан, в четвертом - закон движения

Страница 3 из51

Основные принципы ООП

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

так и определяющих его поведение) в единую программную единицу некий

абстрактный тип (класс).

О г р а н и ч е н и е д о с т у п а - сокрытие отдельных элементов реализации абстракции, не затрагивающих существенных характеристик ее как целого.

Необходимость ограничения доступа предполагает разграничение двух частей в описании абстракции:

интерфейс - совокупность доступных извне элементов реализации абстракции (основные характеристики состояния и поведения);

реализация - совокупность недоступных извне элементов реализации абстракции (внутренняя организация абстракции и механизмы реализации ее поведения).

Ограничение доступа в ООП позволяет разработчику:

выполнять конструирование системы поэтапно, не отвлекаясь на особенности реализации используемых абстракций;

легко модифицировать реализацию отдельных объектов, что в правильно организованной системе не потребует изменения других объектов.

Сочетание объединения всех свойств предмета (составляющих его состояния и поведения) в единую абстракцию и ограничения доступа к реализации этих свойств получило название инкапсуляции.

М о д у л ь н о с т ь - принцип разработки программной системы,

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

Страница 4 из51

Основные принципы ООП

модульного программирования, следование ему упрощает проектирование и

отладку программы.

И е р а р х и я - ранжированная или упорядоченная система абстракций.

Принцип иерархичности предполагает использование иерархий при разработке программных систем.

В ООП используются два вида иерархии.

Иерархия «целое/часть» - показывает, что некоторые абстракции включены

в рассматриваемую абстракцию как ее части, например, лампа состоит из цоколя, нити накаливания и колбы. Этот вариант иерархии используется в процессе разбиения системы на разных этапах проектирования (на логическом уровне - при декомпозиции предметной области на объекты, на физическом уровне - при декомпозиции системы на модули и при выделении отдельных процессов в мультипроцессной системе).

Иерархия «общее/частное» - показывает, что некоторая абстракция является частным случаем другой абстракции, например, « обеденный стол -

конкретный вид стола», а « столы - конкретный вид мебели». Используется при

разработке структуры классов, когда сложные классы строятся на базе более простых путем добавления к ним новых характеристик и, возможно, уточнения имеющихся.

Один из важнейших механизмов ООП - наследование свойств в иерархии общее/частное. Наследование - такое соотношение между абстракциями, когда одна из них использует структурную или функциональную часть другой или нескольких других абстракций (соответственно простое и множественное

наследование).

Т и п и з а ц и я - ограничение, накладываемое на свойства объектов и

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

объявляется тип, который определяет множество операций над

Страница 5 из51

Основные принципы ООП

соответствующим программным объектом. Рассматриваемые далее языки программирования на основе Паскаля используют строгую, а на основе С -

среднюю степень типизации.

Использование принципа типизации обеспечивает:

раннее обнаружение ошибок, связанных с недопустимыми операциями над программными объектами (ошибки обнаруживаются на этапе компиляции программы при проверке допустимости выполнения данной операции над программным объектом);

упрощение документирования;

возможность генерации более эффективного кода.

Тип может связываться с программным объектом статически (тип объекта определен на этапе компиляции - раннее связывание) и динамически (тип объекта определяется только во время выполнения программы -позднее связывание). Реализация позднего связывания в языке программирования позволяет создавать переменные - указатели на объекты, принадлежащие различным классам(полиморфные объекты), что существенно расширяет возможности языка.

П а р а л л е л и з м - свойство нескольких абстракций одновременно находиться в активном состоянии, т.е. выполнять некоторые операции.

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

например, относятся задачи автоматического управления несколькими процессами.

Реальный параллелизм достигается только при реализации задач такого типа на многопроцессорных системах, когда имеется возможность выполнения каждого процесса отдельным процессором. Системы с одним процессором имитируют параллелизм за счет разделения времени процессора между задачами управления различными процессами. В зависимости от типа используемой операционной системы (одноили мультипрограммной)

Страница 6 из51

Основные принципы ООП

разделение времени может выполняться либо разрабатываемой системой (как в

MS DOS), либо используемой ОС (как в системах Windows).

У с т о й ч и в о с т ь - свойство абстракции существовать во времени независимо от процесса, породившего данный программный объект, и/или в пространстве, перемещаясь из адресного пространства, в котором он был создан.

Различают:

∙ временные объекты, хранящие промежуточные результаты некоторых действий, например вычислений;

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

∙ глобальные объекты, существующие пока программа загружена в память;

∙ сохраняемые объекты, данные которых хранятся в файлах внешней памяти между сеансами работы программы.

Все указанные выше принципы в той или иной степени реализованы в различных версиях объектно-ориентированных языков.

Объектно-ориентированные языки программирования.Язык считается объектно-ориентированным, если в нем реализованы первые четыре из рассмотренных семи принципов.

Особое место занимают объектные модели Delphi и C++Builder. Эти модели обобщают опыт ООП для MS DOS и включают некоторые новые средства,

обеспечивающие эффективное создание более сложных систем. На базе этих моделей созданы визуальные среды для разработки приложений Windows.

Сложность программирования под Windows удалось существенно

снизить за счет создания специальных библиотек объектов, « спрятавших» многие элементы техники программирования.

Страница 7 из51

Основные принципы ООП

Этапы разработки программных систем с использованием ООП.

Процесс разработки программного обеспечения с использованием ООП включает четыре этапа: анализ, проектирование, эволюция, модификация.

Рассмотрим эти этапы.

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

Проект ирование .Различают :

логическое проектирование, при котором принимаемые решения практически не зависят от условий эксплуатации (операционной системы и используемого оборудования);

физическое проектирование, при котором приходится принимать во внимание указанные факторы.

Логическое проектирование заключается в разработке структуры классов:

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

композиция, наполнение, полиморфизм и т.д.). Результатом является иерархия или диаграмма классов, отражающие взаимосвязь классов, и описание классов.

Физическое проектирование включает объединение описаний классов в модули, выбор схемы их подключения (статическая или динамическая компоновка), определение способов взаимодействия с оборудованием, с

операционной системой и/или другим программным обеспечением (например,

базами данных, сетевыми программами), обеспечение синхронизации процессов для систем параллельной обработки и т.д.

Страница 8 из51

Основные принципы ООП

Э в о л ю ц и я с и с т е м ы. Это процесс поэтапной реализации и

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

работающий прототип будущей системы. Он тестируется и отлаживается.

Например, таким прототипом может служить система, включающая реализацию основного интерфейса программного продукта (передача сообщений в отсутствующую пока часть системы не выполняется). В результате мы получаем работоспособный прототип продукта, который может быть, например, показан заказчику для уточнения требований. Затем к системе подключается следующая группа классов, например, связанная с реализацией некоторого пункта меню.

Полученный вариант также тестируется и отлаживается, и так далее, до реализации всех возможностей системы.

Использование поэтапной реализации существенно упрощает тестирование и отладку программного продукта.

Модификация. Это процесс добавления новых функциональных возможностей или изменение существующих свойств системы. Как правило,

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

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

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

Страница 9 из51

Основные принципы ООП

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

Обычно проектирование начинается, когда какой-либо фрагмент предметной области достаточно полно описан в процессе анализа.

Рассмотрение основных приемов объектного подхода начнем с объектной декомпозиции.

Объектная декомпозиция

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

происходящие в предметной области поставленной задачи.

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

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

Функциональные элементы системы, параметры и поведение которой определяются условием задачи, обладающие самостоятельным поведением

(т.е. « умеющие» выполнять некоторые действия, зависящие от полученных сообщений и состояния элемента), получили название объектов.

Процесс представления предметной области задачи в виде совокупности объектов, обменивающихся сообщениями, называется объектной декомпозицией.

Страница 10 из51

Основные принципы ООП

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

Пример. Объектная декомпозиция (имитационная модель

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

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

Процесс работы бензоколонки с тремя заправочными местами можно представить в виде диаграммы.



Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: