
- •Объектно-ориентированное программирование
- •Введение
- •Основные положения объектно-ориентированного программирования
- •Принципы ооп
- •Элементы классов
- •Области видимости
- •Свойства
- •Объекты
- •События. Взаимодействие объектов.
- •Парадигма объектно-ориентированного программирования
- •От структурированного программирования к ооп
- •Основные понятия и принципы ооп
Принципы ооп
Инкапсуляция (encapsulation). Работа с данными и детали ее реализации скрыты от внешнего пользователя объекта. Преимущества инкапсуляции заключаются в модульности и изоляции кода объекта от другого кода программы.
Наследование (inheritance). Возможность создания новых объектов, которые наследуют свойства и поведение родительских объектов. Такая концепция позволяет создавать иерархии объектов (например, библиотека VCL), включающие наборы объектов, порожденных от одного общего предка и обладающих все большей специализацией и функциональностью по сравнению со своими предшественниками, но, тем не менее, использующие все возможности родительских классов.
Преимущества наследования заключается, в первую очередь, в совместном использовании многими объектами общего кода. От каких классов унаследован объект Form1, о котором говорилось выше, можно посмотреть, если щелкнуть на пунктах меню View=>Browser (или нажать клавиши <Shift+Ctrl+B>) и щелкнуть на кнопке Classes. Вы увидите иерархию наследования для созданного типа TForm1 (рис. 3.1).
Рис. 3.1. Иерархия наследования для типа TForm1
Видно, что вверху иерархии находится класс TObject, о котором поговорим позже.
Полиморфизм (polymorphism). Слово "полиморфизм" означает "много форм". В данном случае под этим подразумевается, что вызов метода объекта для переменной приведет к выполнению кода, конкретного экземпляра класса, соответствующего данной переменной.
Обо всем этом подробнее поговорим ниже, и начнем с элементов класса.
Элементы классов
Поля
Поле (Field) можно также определить как переменную экземпляра (Instance variable), представляющую собой данные для объекта. Поле в объекте подобно полю в записи языка Pascal, но в отличие от записи, к полям в объекте обычно не разрешен прямой доступ. Это способствует защите полей от случайного или предумышленного искажения.
Доступ к полям происходит через свойства, которые могут выступать как фильтры и не пропускать недопустимых значений. Объявления полей происходят как объявления обычных переменных, при этом поля чаше всего размещаются в закрытом интерфейсе класса, доступ к которому ограничен. В именах полей принято ставить первую букву F, от слова Field, например:
FQuantity: Integer;
Т.о. поля класса являются переменными, объявленными внутри класса. Они предназначены для хранения данных во время работы экземпляра класса (объекта). Ограничений на тип полей в классе не предусмотрено. В описании класса поля должны предшествовать методам и свойствам. Обычно поля используются для обеспечения выполнения операций внутри класса.
Итак, поля предназначены для использования внутри класса. Однако класс должен каким-либо образом взаимодействовать с другими классами или программными элементами приложения.
В подавляющем большинстве случаев класс должен выполнить с некоторыми данными определенные действия и представить результат. Для получения и передачи данных в классе применяются свойства.
Области видимости
Object Pascal предоставляет дополнительный контроль степени доступа к членам классов (полям и методам) с помощью директив protected, private, public, published и automated, открывающих соответствующие разделы объявлений, или, как принято говорить, – интерфейсы класса. За каждой из директив может следовать любое необходимое количество объявлений полей или методов.
Каждый интерфейс обеспечивает следующий уровень доступа.
Private (закрытый). Объявленные в данном разделе переменные и методы доступны только для того кода, который находится в блоке реализации самого объекта. Директива private скрывает особенности реализации объекта от пользователей и защищает члены этого объекта от непосредственного доступа и изменения извне.
Protected (защищенный). Члены класса, объявленные в разделе protected, доступны объектам, производным от данного класса. Это позволяет скрыть внутреннее устройство объекта от пользователя и в то же время обеспечить необходимую гибкость, а также эффективность доступа к полям и методам объекта для его потомков.
Public (открытый). Объявленные в этом разделе члены объекта доступны в любом месте программы. Конструкторы и деструкторы всегда должны быть объявлены как public.
Published (экспортируемый). Для членов объекта, объявленных в данном разделе, в процессе компиляции будет создана информация о типах времени выполнения (RTTI – Runtime Type Information). Это позволит другим элементам приложения получать информацию об элементах объекта, объявленных как published. В частности, подобная информация используется инспектором объектов при построении списков свойств объектов.
Automated (автоматизированный). Этот раздел сохранен только для обеспечения совместимости с Delphi 2.
Методы
Методы (Methods) представляют собой процедуры и функции, принадлежащие классу. Можно сказать, что методы определяют поведение класса.
Итак, методом называется объявленная в классе функция или процедура, которая используется для работы с полями и свойствами класса. Согласно принципам ООП, обращаться к свойствам класса можно только через его методы.
От обычных процедур и функций методы отличаются тем, что им при вызове передается указатель на тот объект, который их вызвал.
В классе всегда должны присутствовать два важнейших метода: конструктор и деструктор.
При проектировании класса можно создать произвольное количество любых других методов, необходимых для решения конкретных задач.
Создание метода – процесс, состоящий из двух этапов. Сначала следует объявить метод в объявлении класса, а затем создать код его реализации.
Например, выполним следующее.
1. Создадим новый проект типа Application.
2. Откроем инспектор объектов (клавиша <F11>) и щелкнем на вкладке Events (События).
3. Два раза щелкнем на поле OnClick.
В редакторе кодов получим следующую программу (см. листинг ниже), где в классе TForm1 появится процедура FormClick, а в разделе реализации – описание этой процедуры.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormClick(Sender: TObject);
begin
... { Здесь что-то выполняется }
end;
end.
Таким образом мы создали пустую процедуру обработки щелчка мыши. Можно ее заполнить соответствующими кодами и получить нужный эффект.
Например, можно изменить цвет фона окна на черный, для чего поместим в описание процедуры TForm1.FormClick (между begin.. .end) следующую строку:
Form1.Color := clBlack;
и запустим программу (клавиша <F9>). После того как появится окно, щелкнем на нем мышью и наблюдаем эффект.
Типы методов
Методы объекта могут быть описаны как статические (static), виртуальные (virtual), динамические (dinamic) или как методы обработки сообщения (message-handling), для чего к ним добавляются соответствующие директивы.
Статические методы. Статические методы работают подобно обычным процедурам или функциям. Этот тип методов устанавливается по умолчанию. Адрес такого метода известен уже на стадии компиляции, и компилятор в коде программы оформляет все вызовы данного метода как статические. Такие методы работают быстрее других, однако не могут быть перегружены в целях полиморфизма объектов.
Виртуальные методы. Вызов виртуальных методов из-за возможности их перегрузки немного сложнее, чем вызов статических, так как во время компиляции адрес конкретного вызываемого метода не известен. Для решения этой задачи компилятор строит таблицу виртуальных методов (VMT – Virtual Method Table), обеспечивающую определение адреса метода в процессе выполнения программы. VMT содержит все виртуальные методы предка и виртуальные методы самого объекта, поэтому виртуальные методы используют несколько больший объем памяти, чем методы динамические, однако их вызов происходит быстрее.
Динамические методы. Динамические методы в целом подобны виртуальным методам, но обслуживаются другой диспетчерской системой. Каждому динамическому методу компилятор назначает уникальное число и использует его вместе с адресом метода для построения таблицы динамических методов (DMT– Dynamic Method Table). В отличие от VMT, DMT содержит методы лишь данного объекта, благодаря чему обеспечивается экономия используемой памяти, но замедляется вызов метода, поскольку для поиска его адреса, скорее всего, будет пересмотрена не одна DMT в иерархии объектов.
Методы обработки сообщения. Методы обработки сообщений предназначены для обработки приходящих сообщений, с помощью которых происходит обмен информацией в операционной системе Windows. Значение после ключевого слова message определяет сообщение, в ответ на которое вызывается данный метод. Такие методы создаются для реакции на те или иные сообщения Windows. Они никогда не вызываются непосредственно из программы.
Например:
type
TTextBox = class(TCustomControl)
private
procedure WMCharfvar Message: TWMChar); message WM_CHAR;
end;
где WM_CHAR – константа, определяющая данное сообщение. Две буквы WM (Windows Message) говорят о том, что это сообщение операционной системы Windows. В данном случае это связано с нажатием клавиш клавиатуры.
Методы класса
Для использования каждого метода необходимо сначала создать объект (экземпляр класса) и только потом делать вызов метода в программе, для чего используются имя объекта и имя метода, написанные через точку.
Например, в ранее приведенном классе TForm1 вызов метода FormClick просходит как TForml.FormClick. Но если перед объявлением метода поставить ключевое слово class, то этот метод может быть вызван как обычная процедура или функция без создания экземпляра класса, членом которого является данный метод. (Аналоги таких методов в языке C++ объявляются как static.)
Необходимо иметь в виду, что при создании подобных методов нельзя использовать никакой информации экземпляра класса, поскольку это приведет к ошибке компиляции.
Переопределение методов
Переопределение (overriding) методов в Object Pascal реализует концепцию полиморфизма, позволяя изменять поведение метода от наследника к наследнику.
Переопределение метода возможно только в том случае, если первоначально он был объявлен как virtual или dynamic.
Для переопределения метода при его объявлении вместо ключевых слов virtual или dynamic следует указать ключевое слово override.
Ниже приведен пример переопределения методов, где метод Draw переопределен в двух унаследованных классах TRectangle и TEllipse.
Type
TFigure = class
procedure Draw; virtual;
end;
TRectangle = class(TFigure)
procedure Draw; override;
end;
TEllipse = class(TFigure)
procedure Draw; override;
end;
Здесь первым, объявлен класс TFigure, который затем наследуют классы TRectangle и TEllipse, что видно из объявлений классов, где за ключевым словом class в круглых скобках помещается имя родительского класса.
Директива override приводит к замещению строки описания исходного метода в VMT строкой описания нового метода. Если объявить новые функции с ключевым словом virtual или dynamic, а не override, то вместо замещения старых будут созданы новые методы.
В случае переопределения статического метода новый вариант просто полностью заменит статический метод родителя.
Если использовать следующий исполнимый код:
var
Figure: TFigure;
begin
Figure := TRectangle.Create;
Figure.Draw; // вызывает TRectangle.Draw
Figure.Destroy;
Figure := TEllipse.Create;
Figure.Draw; // вызывает TEllipse.Draw
Figure.Destroy;
end;
то, в зависимости от того, какая фигура создается, прямоугольник (TRectangle) или эллипс (TEllipse), вызываются разные методы для их прорисовки, хотя способ вызова один и тот же: Figure.Draw;.
Перегрузка методов
Подобно обычным процедурам и функциям, методы могут быть перегружены таким образом, чтобы класс содержал несколько методов с одним именем, но с различными списками параметров.
Перегруженные методы должны быть объявлены с указанием директивы overload. Вот пример объявления класса с перегруженными методами.
Type
TSomeClass = class
procedure AMethod(I: Integer); overload;
procedure AMethod(S: string); overload;
procedure AMethod(D: Double); overload;
end;
Дублирование имен методов
Иногда может понадобиться к одному из классов добавить метод, замещающий метод с тем же именем, но принадлежащий предку этого класса. В данном случае требуется не переопределить исходный метод, а полностью его заменить. Если просто добавить такой метод в новый класс, то компилятор выдаст предупреждение о том, что новый метод скрывает метод базового класса с тем же именем. Для устранения этой ошибки в новом методе укажите директиву reintroduce.
type
T1 = class(TObject)
procedure Test(I: Integer); overload; virtual;
end;
T2 = class(Tl)
procedure Test(S: string); reintroduce; overload;
end;
В следующем коде будут вызываться различные методы.
SomeObject := Т2.Create;
SomeObject.Test('Hello!'); // вызывается T2.Test
SomeObject.Test(7); // вызывается Tl.Test
Указатель Self
Во всех методах класса доступна неявная переменная Self, представляющая собой указатель на тот экземпляр объекта, который был использован при данном вызове этого метода. Переменная Self передается методу компилятором в качестве скрытого параметра, например:
function TCollection.Add: TCollectionItem;
begin
Result := FItemClass.Create(Self);
end;
где функция Add создает объект типа TCollectionItem для класса, на который происходит ссылка в поле FItemClass и который всегда является наследником TCollectionltem. Например, в кодах:
var MyCollection: TCollection;
…
MyCollection.Add
Переменная MyCollection передается в метод TCollectionltem.Create.