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

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

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

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

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

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

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

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

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

ООП характеризуется следующими принципами ( по Алану Кею):

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

Определение 10.1 . Абстрагирование (abstraction) - метод решения задачи, при котором объекты разного рода объединяются общим понятием (концепцией), а затем сгруппированные сущности рассматриваются как элементы единой категории.

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

Определение 10.2 . Инкапсуляция (encapsulation) - техника, при которой несущественная с точки зрения интерфейса объекта информация прячется внутри него.

Определение 10.3 . Наследование (inheritance) - свойство объектов, посредством которого экземпляры класса получают доступ к данным и методам классов-предков без их повторного определения.

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

Определение 10.4 .

Всем привет.

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

Начнем.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Из своего опыта могу сказать, что всегда считал что понимал ООП, что же тут такого то - полиморфизм, инкапсуляция и наследование, но вот когда дошло до дела, то туговато пришлось. Хочу разложить всё по полочкам чтобы никто не наступил на мои грабли в будущем:)

Шаг 1.

Немного теории:

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

В центре ООП находится понятие объекта.

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

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

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

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

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

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

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

Полиморфизм - это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Шаг 2.

Инкапсуляция.

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

Зачем же это нужно?

Думаю, вам бы не хотелось, чтобы кто-то, что-то изменял в написанной вами библиотеки.

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

Цель инкапсуляции – уйти от зависимости внешнего интерфейса класса (то, что могут использовать другие классы) от реализации. Чтобы малейшее изменение в классе не влекло за собой изменение внешнего поведения класса. Давайте рассмотрим, как ею пользоваться.

Существует 4 вида модификаторов доступа: public , protected , private и default .

Public – уровень предполагает доступ к компоненту с этим модификатором из экземпляра любого класса и любого пакета.

Protected – уровень предполагает доступ к компоненту с этим модификатором из экземпляров родного класса и классов-потомков, независимо от того, в каком пакете они находятся.

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

Private – уровень предполагает доступ к компоненту с этим модификатором только из этого класса.

Public class Human { public String name; protected String surname; private int age; int birthdayYear; }

public String name; - имя, которое доступное из любого места в приложении.
protected String surname; - фамилия доступна из родного класса и потомков.
private int age; - возраст доступен только в рамках класса Human.
int birthdayYear; - хоть не указывается явный модификатор доступа, система понимает его как default, год рождения будет доступен всему пакету, в котором находится класс Human.

Для разных структурных элементов класса предусмотрена возможность применять только определенные уровни модификаторов доступа.

Для класса - только public и default.

Для атрибутов класса - все 4 вида.

Для конструкторов - все 4 вида.

Для методов - все 4 вида.

Шаг 3.

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

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

Наследование является важным, поскольку оно позволяет поддерживать концепцию иерархии классов (hierarchical classification). Применение иерархии классов делает управляемыми большие потоки информации.

Разберем этот механизм на классическом примере: Геометрические фигуры.

У нас есть интерфейс Figure:

Public interface Figure { public void draw (); public void erase (); public void move (); public String getColor (); public boolean setColor (); }

Интерфейс (более детально будут рассмотрены в скором будущем ) - нам говорит, как должен выглядеть класс, какие методы в себе содержать, какими переменными и типами данных манипулировать. Сам интерфейс не реализует методы, а создает как бы скелет для класса, который будет расширять этот интерфейс. Есть класс Figure, который расширяет интерфейс Figure:

Public class Figure implements сайт.oop.inheritance.interfaces.Figure{ @Override public void draw() { //need to implement } @Override public void erase() { //need to implement } @Override public void move(int pixel) { //need to implement } @Override public String getColor() { return null; } @Override public boolean setColor(String colour) { return false; } }

В этом классе мы реализуем все методы интерфейса Figure .

public class Figure implements сайт.oop.inheritance.interfaces.Figure - с помощью ключевого слова implements мы перенимаем методы интерфейса Figure для реализации.

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

И соответственно у нас есть 3 класса самих фигур, которые наследуются от класса Figure. Класс Figure является родительским классом или классом-родителем, а классы Circle, Rectungle и Triangle - являются дочерними.

Public class Circle extends Figure { @Override public void draw() { super.draw(); } @Override public void erase() { super.erase(); } @Override public void move(int pixel) { super.move(pixel); } @Override public String getColor() { return super.getColor(); } @Override public boolean setColor(String colour) { return super..oop.inheritance.Figure{ @Override public void draw() { super.draw(); } @Override public void erase() { super.erase(); } @Override public void move(int pixel) { super.move(pixel); } @Override public String getColor() { return super.getColor(); } @Override public boolean setColor(String colour) { return super..oop.inheritance.Figure{ @Override public void draw() { super.draw(); } @Override public void erase() { super.erase(); } @Override public void move(int pixel) { super.move(pixel); } @Override public String getColor() { return super.getColor(); } @Override public boolean setColor(String colour) { return super.setColor(colour); } }

public class Triangle extends сайт.oop.inheritance.Figure - это значит, что класс Triangle наследует класс Figure .

super.setColor(colour); - super модификатор, позволяющий вызывать методы из класса родителя.

Теперь каждый класс перенял свойства класса Figure. Что собственно это нам дало?

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

Наверное возник вопрос: чем же extends отличается от implements ?

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

В дочерних классах мы можем спокойно добавлять новые интересующие нас методы. Например, мы хотим добавить в класс Triangle 2-а новых метода: flimHorizontal () и flipVertical ():

/** * New Method */ public void flipVertical () { }; /** * New Method */ public void flipHorizontal () { };

Теперь эти 2-а метода принадлежат сугубо классу Triangle . Этот подход используется когда базовый класс не может решить всех проблем.

Или можно использовать другой подход, изменить или переписать методы в дочерним классе:

Довольно интересный факт: в java запрещено множественное наследование, но любой из классов по умолчанию наследуется то класса Object . То есть при наследовании любого класса у нас получается множественное наследование)

Но не стоит забивать этим голову!

Шаг 4.

Полиморфизм.

В более общем смысле, концепцией полиморфизма является идея “один интерфейс, множество методов “.

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

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

Public class Parent { int a = 2; } public class Child extends Parent { int a = 3; }

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

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

Компилятор может опираться только на тип ссылки, с помощью которой происходит обращение к полю:

Child c = new Child(); System.out.println(c.a); // результатом будет 3 Parent p = c; System.out.println(p.a); //результатом будет 2

Данное объявление так и называется – «скрывающим ». Родительское поле продолжает существовать.

К нему можно обратиться явно:

Class Child extends Parent { int a = 3; //скрывающее объявление int b = ((Parent)this).a; //громоздкое обращение к родительскому полю int c = super.a; //простое обращение к родительскому полю }

Переменные b и c получат значения, родительского поля a . Хотя выражение с super более простое, оно не позволит обратиться на два уровня вверх по дереву наследования.

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

К нему можно обратиться явным приведением, как это делается для b .

Class Parent { int x = 0; public void printX() { System.out.println(x); } } class Child extends Parent { int x = -1; }

Каков будет результат для new Child.printX() ; ?

Метод вызывается с помощью ссылки типа Child , но метод определен в классеParent и компилятор расценивает обращение к полю x в этом методе именно как к полю класса Parent . Результатом будет 0 .

Рассмотрим случай переопределения методов:

Class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } } Child c = new Child(); System.out.println(c.getValue()); // результатом будет 1 Parent p = c; System.out.println(p.getValue()); // результатом будет 1

Родительский метод полностью перекрыт.

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

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

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

Шаг 5.

Абстракция:

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

В контексте ООП абстракция - это обобщение данных и поведения для типа, находящегося выше текущего класса по иерархии.

Перемещая переменные или методы из подкласса в супер класс, вы обобщаете их. Это общие понятия, и они применимы в языке Java. Но язык добавляет также понятия абстрактных классов и абстрактных методов .

Абстрактный класс является классом, для которого нельзя создать экземпляр.

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

Издаваемый звук зависит от вида животного.

Как это смоделировать?

Определить общее поведение в абстрактном классе и заставить подклассы реализовывать конкретное поведение, зависящее от их типа.

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

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

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

Public abstract class Person { abstract void move(); abstract void talk(); } public class Adult extends Person { public Adult() { } public void move() { System.out.println("Walked."); } public void talk() { System.out.println("Spoke."); } } public class Baby extends Person { public Baby() { } public void move() { System.out.println("Crawled."); } public void talk() { System.out.println("Gurgled."); } }

Что мы сделали в приведенном выше коде?

Мы изменили Person и указали методы как abstract , заставив подклассы реализовывать их.
Мы сделали Adult подклассом Person и реализовали эти методы.
Мы сделали Baby подклассом Person и реализовали эти методы.

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

Теперь, поскольку Adult и Baby являются подклассами Person , мы можем обратиться к экземпляру каждого класса как к типу Person.

Абстрактные типы данных

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

Рассмотрим пример. Все мы смотрим телевизионные про­граммы. Назовем телевизор модулем или объектом. Этот объект имеет интерфейс с пользователем, т. е. средства управ­ления (совокупность кнопок), воспроизведения изображения и звука. Чем совершеннее интерфейс, тем удобнее телевизор в использовании. Мы переключаем программы, нажимая опре­деленные кнопки, и при этом не задумываемся о физических процессах, происходящих в телевизоре. Об этом знают спе­циалисты. Когда мы выбираем телевизор, нас интересуют его цена и эксплуатационные параметры, т. е. качество изобра­жения, звука и т. п. Однако нас не интересует то, что находит­ся внутри. Другими словами, мы возвращаемся к свойствам объекта (модуля), какими являются интерфейс и реализация. Основная цель абстракции в программировании как раз и заключается в отделении интерфейса от реализации.

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

Абстракция данных предполагает определение и рассмот­рение абстрактных типовданных (АТД) или, что то же самое, новых типов данных, введенных пользователем.

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

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

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

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

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

Как известно, одним из принципов управления сложностью проекта является декомпозиция. Гради Буч выделяет две разновидности декомпозиции: алгоритмическую (так он называет декомпозицию, поддерживаемую структурными методами) и объектно-ориентированную, отличие которых состоит, по его мнению, в следующем: «Разделение по алгоритмам концентрирует внимание на порядке происходящих событий, а разделение по объектам придает особое значение факторам, либо вызывающим действия, либо являющимся объектами приложения этих действий».

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

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

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

Объекты и классы

Базовыми блоками объектно-ориентированной програм­мы являются объекты и классы. Содержательно объект мож­но представить как что-то ощущаемое или воображаемое и имеющее хорошо определенное поведение. Таким образом, объект можно либо увидеть, либо потрогать, либо, по край­ней мере, знать, что он есть, например, представлен в виде информации, хранимой в памяти компьютера. Дадим определение объекта, придерживаясь мнения Гради Буча: «Объект – осязаемая сущность, которая четко проявляет свое поведение».

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

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

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

Определим теперь понятия состояния, поведения и иденти­фикации объекта.

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

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

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

Выше уже говорилось, что процедурный (а также и мо­дульный) подход позволяет строить программы, состоящие из набора процедур (подпрограмм), реализующих заданные алгоритмы. С другой стороны, объектно-ориентированный подход представляет программы в виде набора объектов, взаимодействующих между собой. Взаимодействие объектов осуществляется через сообщения. Предположим, что нашим объектом является окружность. Тогда сообщение, посланное этому объекту, может быть следующим: «нарисуй себя». Ко­гда мы говорим, что объекту передается сообщение, то на самом деле мы вызываем некоторую функцию этого объекта (компонент-функцию). Так, в приведенном выше примере мы вызовем функцию, которая будет рисовать окружность на экране дисплея.

Базовые принципы ООП

К базовым принципам объектно-ориентированного стиля программирования относятся:

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

Пакетирование (инкапсуляция)

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

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

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

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

Наследование

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

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

По Гради Бучу «наследование – это такое отношение между объектами, когда один объект повторяет структуру и поведение другого».

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

Типы верхних уровней объектно-ориентированной иерархии, как правило, не имеют конкретных экземпляров объектов. Не существует, например, конкретного живого организма, который бы сам по себе назывался «млекопитающее» или «птица». Такие типы называют абстрактными. Конкретные экземпляры объектов имеют, как правило, типы самых нижних уровней ОО-иерархии: «крокодил Гена» – конкретный экземпляр объекта типа «крокодил», «кот Матроскин» – конкретный экземпляр объекта типа «кошка».

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

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

Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.

Когда вы строите новый класс, наследуя его из сущест­вующего класса, можно:

  • добавить в новый класс новые компоненты-данные;
  • добавить в новый класс новые компоненты-функции;
  • заменить в новом классе наследуемые из старого класса компоненты-функции.

Полиморфизм

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

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

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

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

Таким образом, в нашем примере с объектами-животными действие «бежать» будет называться полиморфическим действием, а многообразие форм проявления этого действия – полиморфизмом.

Описание объектного типа

Класс или объект – это структура данных, которая содержит поля и методы. Как всякая структура данных она начинается зарезервированным словом и закрывается оператором end . Формальный синтаксис не сложен: описание объектного типа получается, если в описании записи заменить слово record на слово object или class и добавить объявление функций и процедур над полями.

Type <имя типа объекта>= object
<поле>;
<поле>;
….
<метод>;
<метод>;
end ;

В ObjectPascal существует специальное зарезервированное слово class для описания объектов, заимствованное из С++.

Type <имя типа объекта>= class
<поле>;
….
<метод>;
<метод>;
end ;

ObjectPascal поддерживает обе модели описания объектов.

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

Пример . Вводится объектный тип «предок», который имеет поле данных Name (имя) и может выполнять два действия:

  • провозглашать: «Я – предок!»;
  • сообщать свое имя.

Type tPredoc = object Name: string ; {поле данных объекта}
Procedure Declaration ; {объявление методов объекта}
Procedure MyName ;
End ;

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

Procedure tPredoc.Declaration ; {реализация метода объекта}
begin
writeln ("Я – предок!");
end ;
Procedure tPredoc.MyName ; {реализация метода объекта}
begin
writeln("Я –", Name);
end;

Внутри описания методов на поля и методы данного типа ссылаются просто по имени. Так метод MyName использует поле Name без явного указания его принадлежности объекту так, если бы выполнялся неявный оператор with <переменная_типа_объект> do .

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

…….{объявление объектного типа и описание его методов}
var v 1: tPredoc ; {объявление экземпляра объекта}
begin
v1. Name:= "Петров Николай Иванович";
v1.Declaration;
v1.MyName
end.

Использование поля данных объекта v1 не отличается по своему синтаксису от использования полей записей. Вызов методов экземпляра объекта означает, что указанный метод вызывается с данными объекта v 1. В результате на экран будут выведены строчки

Я – предок!
Я – Петров Николай Иванович

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

Например, в тексте программы вместо операторов

возможно использование оператора with такого вида

with v1 do
begin
Name:= "Петров Николай Иванович";
Declaration ;
MyName
End ;

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

Иерархия типов (наследование)

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

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

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

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

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

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

Наиболее часто структура иерархии классов описывается в виде дерева. Вершины дерева соответствуют классам, а корню соответствует класс, который описывает что-то общее (самое общее) для всех других классов.

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

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

Правило 2 . Доступ к полям и методам родительских типов в рамках описания любых дочерних типов выполняется так, как будто-бы они описаны в самом дочернем типе.

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

Правило 4 . Дочерний тип может доопределить произвольное число собственных методов и информационных полей.

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

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

Продолжим рассмотрение нашего примера. В дополнение к введенному нами типу предка tPredoc можно ввести типы потомков:

tуре tSon= оbject(tPredoc) {Тип, наследующий tPredoc }
procedure Declaration; {перекрытие методов предка}
procedure Му Name(Predoc: tPredoc);
end ;

Tуре tGrandSon=object(tSon) {Тип, наследующий tSon}
procedure Declaration ; {перекрытие методов предка}
end ;

Имя типа предка приводится в скобках после слова оbject. Мы породили наследственную иерархию из трех типов: tSon («сын») наследник типу tPredoc , а тип tGrandSon (“внук”) ­- типу tSon. Тип tSon переопределяет методы Declaration и Му N а m е, но наследует поле Name . Тип tGrandSon переопределяет только метод Declaration и наследует от общего предка поле Name , а от своего непосредственного предка (типа tSon) переопределенный метод Declaration .

Давайте разберемся, что именно мы хотим изменить в родительских методах. Дело в том, что «сын» должен провозглашать несколько иначе, чем его предок, а именно сообщить "Я – отец!"

procedure tSon.Declaration ; {реализация методов объектов - потомков}
begin
writeln (" Я - отец!");
end;

А называя свое имя, “сын” должен сообщить следующие сведения:

  • Я <фамилия имя отчество >
  • Я – сын <фамилия имя отчество своего предка>

procedure tSon .Му Name (predoc: tPredoc);
begin
inherited Му Name ; {вызов метода непосредственного предка}
writeln ("Я - сын ", predoc.Name, " а ");
end;

В нашем примере потомок tSon из метода Му Name вызывает одноимен­ный метод непосредственного предка типа tPredoc . Такой вызов обес­печивается директивой inherited , после которой указан вызываемый метод непосредственного предка. Если возникает необходимость вызвать метод отдаленного предка в каком-нибудь дочернем типе на любом уровне иерархии, то это можно сделать с помощью уточненного идентификатора, т.е. указать явно имя типа родительского объекта и через точку – имя его метода:

Теперь давайте разберемся с «внуком». Метод, в котором «внук» называет свое имя, в точности такой же, как и у его непосредственного предка (типа tSon), поэтому нет необходимости этот метод переопределять, этот метод лучше автоматически наследовать и пользоваться им как своим собственным. А вот в методе Declaration нужно провозгласить "Я – внук!", поэтому метод придется переопределить.

procedure tGrandSon.Declaration;
begin
writeln (" Я - внук!");
end;

Рассмотрим пример программы, в которой определим экземпляр типа tPredoc , назовем его «дед», экземпляр типа tSon – «отец», и экземпляр типа tGrandSon – «внук». Потребуем от них, чтобы они представились.

Пример программы с испльзованием ООП

{заголовок программы}
……………….
{раздел описания типов, в том числе и объектных типов tPredoc , tSon , tGrandSon }
{Обратите внимание! Экземпляры объектных типов можно описать как типизированные константы, что мы для примера и сделали ниже}
const ded: tPredoc = (Name: "Петров Николай Иванович");
otec: tSon = (Name: "Петров Сергей Николаевич");
vnuk: tGrandSon = (Name: "Петров Олег Сергеевич");
{раздел описания процедур и функций, где обязательно должны быть написаны все объявленные в объектных типах методы}
begin
ded.Declaration ; {вызов методов общего предка}
ded.Му Name;
writeln;
otec.Declaration;
otec.MyName(ded); { вызов методов объекта otec типа tSon}
writeln;
vnuk.Declaration; { вызов методов объекта vnuk типа tGrandSon}
vnuk.MyName (otec);
end .

Наша программа выведет на экран:

Пример вывода на экран результата

Я -предок!
Я -Петров Николай Иванович

Я -отец!
Я -Петров Сергей Николаевич
Я -сын Петров Николай Ивановича

Я -внук!
Я -Петров Олег Сергеевич
Я -сын Петров Сергей Николаевича

Обратите внимание, что в заголовке процедуры tSon . MyName в качестве параметра приведен тип данных tPredoc , а при использовании этой процедуры ей передаются переменные как типа tPredoc , так и типа tSon . Это возможно, так как пре­док совместим по типу со своими потомками. Обратное несправедливо. Если мы заменим в заголовке процедуры tSon . MyName при описании параметров тип tPredoc на tSon , компилятор укажет на несовместимость типов при использовании перемен­ной ded в строке otec . MyName (ded).

Полиморфизм и виртуальные методы

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

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

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

В рассмотренном выше примере во всех трех объектных типах tPredoc , tSon и tGrandSon действуют одноименные методы Declaration и MyName . Но в объектном типе tSon метод MyName выполняется несколько иначе, чем у его предка. А все три одноименных метода Declaration для каждого объекта выполняются по-своему.

Методы объектов бывают статическими, виртуальными и динамическими.

Статические методы

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

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

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

Виртуальные методы

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

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

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

procedure Method (список параметров); virtual;

Использование виртуальных методов в иерархии типов объектов имеет определенные ограничения:

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

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

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

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

constructor Method (список параметров);

Зарезервированное слово constructor заменяет слова procedure и virtual .

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

У каждого объекта своя таблица виртуальных методов VMT . Именно это и позволяет одноименному методу вызывать различные процедуры.

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

Заголовок деструктора выглядит таким образом:

destructor Done ;

Основное назначение деструкторов – уничтожение VMT данного объекта. Часто деструктор не выполняет других действий и представляет собой пустую процедуру.

destructor Done ;
begin end ;

(как расшифровывается ООП) – это, прежде всего, парадигма программирования.
Парадигма программирования определяет то, как программист видит выполнение программы.
Так, для парадигмы ООП характерно, что программист рассматривает программу в виде набора взаимодействующих объектов, в то время как, например, в функциональном программировании программа представляется в виде последовательности вычисления функций. Процедурное программирование или, как его еще правильно называют, классическое операциональное, подразумевает написание алгоритма для решения задачи; при этом ожидаемые свойства конечного результата не описываются и не указываются. Структурное программирование в основном придерживается тех же принципов, что и процедурное, лишь немного дополняя их полезными приемами.
Парадигмы непроцедурного программирования, к которым можно отнести объектно-ориентированную парадигму, имеют совершенно другие идеи.
Определение Гради Буча гласит: “Объектно-ориентированное программирование – это методология программирования, которая основана на представлении программы в виде совокупности объектов, каждый из которых является реализацией определенного класса (типа особого вида), а классы образуют иерархию на принципах наследуемости”.
Структурное и объектно-ориентированное программирование строятся на таком научном методе как декомпозиция — метод, который использует структуру задачи и позволяет разбить решение общей большой задачи на решение последовательности меньших задач. Декомпозиция ООП происходит не по алгоритмам, а по объектам, использующимся при решении задачи. Данная декомпозиция уменьшает размер программных систем благодаря повторному использованию общих механизмов. Известно, что системы визуального программирования или системы, построенные на принципах объектно-ориентированного программирования, являются более гибкими и легче эволюционируют со временем.

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

Сегодня существует достаточное количество объектно-ориентированных языков программирования , наиболее популярными из которых в настоящее время являются C++, Delphi, Java, Visual Basic, Flash. Но, кроме того, многие языки, которые принято причислять к процедурной парадигме, тоже обладают свойствами ООП, имея возможность работать с объектами. Так, объектно-ориентированное программирование в C — это большой раздел программирования на данном языке, то же самое касается ООП в python и многих других структурных языках.

Говоря об ООП, часто всплывает еще одно определение — визуальное программирование . Оно дополнительно предоставляет широкие возможности использования прототипов объектов, которые определяются как классы объектов.
События. Во многих средах визуального программирования реализована характеристика (помимо инкапсуляции, полиморфизма и наследования) объекта – событие. Событиями в объектно-ориентированном программировании называется возможность обработки так называемых сообщений (или событий), получаемых от операционной системы Windows или самой программы. Данный принцип характерен для всех компонентов среды, которые обрабатывают различные события, возникающие в процессе выполнения программы. По сути, событие — это некоторое действие, которое активизирует стандартную реакцию объекта. Событием может рассматриваться, например, щелчок по кнопке мыши, наведение курсора мыши на пункт меню, открытие вкладки и т.п. Очередность выполнения тех или иных действий определяется как раз таки событиями, возникающими в системе, и реакцией на них объектов.
Классы и объекты в ООП — различные понятия. Понятие класса в ООП – это тип данных (такой же как, например, Real или String), а объект – конкретный экземпляр класса (его копия), хранящийся в памяти компьютера как переменная соответствующего типа.
Класс является структурным типом данных. Класс включает описание полей данных, а также процедур и функций, которые работают с этими полями данных. Метод ООП – это и есть такие процедуры и функции применительно к классам.
Классы имеют поля (как тип данных запись — record), свойства, которые похожи на поля, но имеют дополнительные описатели, определяющие механизмы записи и считывания данных и методы — подпрограммы, которые направленны на изменение полей и свойств класса.

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

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

Принцип наследования ООП и подклассы

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

Полиморфизм ООП

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

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