Лекция 12 ООП в языке Object Pascal
Объекты в Object Pascal l l l В языке Object Pascal для совместимости с языком Borland Pascal сохранен тип данных object. Однако, данный тип уже не удовлетворяет современной концепции объектноориентированного подхода к программированию. Поэтому в Object Pascal введен новый объектный тип данных – class. Все визуальные компоненты системы Delphi имеют тип class. В литературе распространен термин «класс» обозначающий тип данных, созданный на основе типа class. Для переменных «классового» типа используется термин «объект» или «экземпляр реализации класса» . В языке Borland Pascal обе этих сущности носят название «объект» . Например: TMemo – «класс» ; var memo 1, mm 2: TMemo – «объекты» , «экземпляры класса» . Данная классификация является условной, поэтому в предудущих лекциях при описании «классов» было использовано общее понятие «объект» .
Тип class и его структура l 1. 2. 3. l l l l Так же как и старый объектный тип object, тип class подчиняется трем принципам объектно-ориентированного программирования: Инкапсуляция Наследование Полиморфизм Описание класса схоже с описанием объекта: Type TType=class <поля класса>; <методы класса>; <свойства класса>; end; Как несложно заметить в классе по сравнению с объектом добавляется новая сущность – свойства класса.
Инкапсуляция l l l Класс представляет собой единство трех сущностей - полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от остальных частей программы, сделать его “самодостаточным” для решения конкретной задачи. В результате класс всегда несет в себе некоторую функциональность. Например, класс TForm содержит (инкапсулирует в себе) все необходимое для создания Windows -окна, класс TMemo представляет собой полнофункциональный текстовый редактор, класс TTimer обеспечивает работу программы с таймером и т. д. Инкапсуляция представляет собой мощное средство обмена готовыми к работе программными заготовками. Библиотека классов Delphi - это фактически набор “кирпичиков”, созданных для построения ваших программ.
Наследование l l l l Любой класс может быть порожден от другого класса. Для этого при его объявлении указывается имя класса-родителя: TCircle= class (TPoint) Порожденный класс автоматически наследует поля, методы и свойства своего родителя и может дополнять их новыми. Таким образом, принцип наследования обеспечивает поэтапное создание сложных классов и разработку собственных библиотек классов. Все классы Object Pascal порождены от единственного родителя класса TObject. Этот класс не имеет полей и свойств, но включает в себя методы самого общего назначения, обеспечивающие весь жизненный цикл любых объектов - от их создания до уничтожения. Программист не может создать класс, который не был бы потомком TObject. Следующие два объявления идентичны: TPoint = class(TObject) TPoint = class Принцип наследования приводит к созданию ветвящегося дерева классов, постепенно разрастающегося при перемещении от TObject к его потомкам. Каждый потомок дополняет возможности своего родителя новыми и передает их своим потомкам.
Полиморфизм l l l Полиморфизм - это свойство классов решать схожие по смыслу проблемы разными способами. В рамках Object Pascal поведенческие свойства класса определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках класса, программист может придавать этим потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, т. е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющих разную алгоритмическую основу и, следовательно, придающих объектам разные свойства. Это и называется полиморфизмом объектов. В Object Pascal полиморфизм достигается не только описанным выше механизмом наследования и перекрытия методов родителя, но и их виртуализацией, позволяющей родительским методам обращаться к методам своих потомков.
Поля l l l l l Полями называются инкапсулированные в классе данные. Поля могут быть любого типа, в том числе - классами, например: type TPoint = class x, y: Integer; c: TColor; end; Каждый объект получает уникальный набор полей, но общий для всех объектов данного класса набор методов и свойств. Фундаментальный принцип инкапсуляции требует обращаться к полям только с помощью методов и свойств класса. Однако в Object Pascal разрешается, однако не рекомендуется, обращаться к полям и напрямую. Доступ к полям осуществляется при помощи составных имен: Pt. x: =100; Класс-потомок получает все поля всех своих предков и может дополнять их своими, но он не может переопределять их или удалять.
Методы l l l l l Инкапсулированные в классе процедуры и функции называются методами. Они объявляются так же, как и обычные подпрограммы: type TPoint = class(TGrob) procedure show; end; Доступ к методам класса, как и к его полям, возможен с помощью составных имен. Методы класса могут перекрываться в потомках: TCircle = class(TPoint) procedure show; end; Потомки обоих классов могут выполнять сходную по названию процедуру show, но, в общем случае, будут это делать по-разному. Такое замещение методов называется статическим, т. к. реализуется компилятором.
Позднее связывание l l l В Object Pascal гораздо чаще используется динамическое замещение методов на этапе прогона программы. Для реализации этого метод, замещаемый в родительском классе, должен объявляться как виртуальный (virtual). В классе-потомке замещающий метод объявляется с директивой override (перекрыть). type TPoint = class(TGrob) procedure show; procedure hide; procedure draw; virtual; end; TCircle = class(TPoint) procedure draw; override end;
Абстрактные методы l l l l Динамически перекрываемые методы часто могут вообще ничего не делать. Такие методы называются абстрактными, они обязаны перекрываться в потомках. Программист может запретить вызов абстрактного метода, объявив его с директивой abstract: type TGrob = class x, y: integer; c: TColor; procedure show; procedure hide; procedure draw; virtual; abstract; end; TPoint = class (TGrob) procedure draw; override; end; TCircle = class(TPoint) procedure draw; override; end; Для абстрактных методов не требуется дополнительная визуализация.
Конструкторы и деструкторы l l l Так как все классы являются динамическими объектами, в состав любого класса входят два специальных метода конструктор и деструктор. У класса TObject эти методы называются Сreate и Destroy, так же они называются в подавляющем большинстве его потомков. Конструктор создает объект и распределяет его в динамической памяти. Деструктор удаляет объект из памяти и уничтожает его. Обращение к конструктору должно предварять любое обращение к полям и некоторым методам объекта. По своей форме конструкторы и деструкторы являются процедурами, но объявляются с помощью зарезервированных слов Сonstructor и Destructor. Любые поля объекта, а также методы класса, оперирующие с его полями, могут вызываться только после создания объекта с помощью вызова конструктора.
Работа с конструктором и деструктором l l Вызов конструктора и деструктора несколько отличается от соответствующих действий над типом object. В конструкторе класса-потомка рекомендуется сначала вызвать конструктор своего родителя, а уже затем осуществлять дополнительные действия. Вызов любого метода родительского класса достигается с помощью зарезервированного слова inherited (унаследованный). Если конструктор или, что особенно важно, деструктор не требует дополнительных действий, можно использовать конструктор или деструктор предка. Вместо вызова деструктора удобнее вызывать стандартную процедуру Free, которая сначала проверяет объект на существование, а затем уже вызывает деструктор.
Пример работы с конструктором и деструктором Пример: Type TPoint = class x, y: integer; constructor create(x 1, y 1: integer); end; Var p: Tpoint; Constructor TPoint. Create(x 1, y 1: integer); begin Inherited Create; // вызывает конструктор объекта-родителя x: =x 1; y: =y 1; end; Begin //начало программы (обработчика) p: =TPoint. Create(100, 100); //вызов конструктора p. Free; //вызов стандартной процедуры free. Деструктор унаследован от объекта-предка. End.
Перегружаемые методы l l l l Перегружаемые или одноименные методы - методы с одинаковыми названиями, существующие в рамках одного класса. Каждый из перегружаемых методов необходимо отметить директивой overload. Каждый из перегружаемых методов может содержать различный набор параметров. Это бывает удобно при необходимости создания различных вариантов одной процедуры, например разные варианты конструкторов: Type TPoint=class(TGrob) constructor Create(x, y: integer); overload; constructor Create(x, y: integer; c: TColor); overload; end; В данном примере описаны два варианта конструктора. В первом задаются начальные координаты точки, во втором еще и ее цвет. Для блокирования сообщений компилятора о повторном заданий метода можно перед overload добавить директиву reintroduce.
Свойства l l l l Свойства - это специальный механизм классов, регулирующий доступ к полям. Свойства объявляются с помощью зарезервированных слов property, read и write. Обычно свойство связано с некоторым полем и указывает те методы класса, которые должны использоваться при записи в это поле или при чтении из него. Если специальных методов не нужно можно использовать поля. Пример: Type TPoint=class xx, yy: integer; function getx: integer; procedure setx(x 1: integer); property x: integer read getx write setx; property x: integer read xx write xx; // два варианта свойства end; В программе свойство ведет себя как обычное поле. Var p: TPoint; Вegin//начало программы (обработчика) P: =TPoint. Create; p. x: =100; //доступ к полю при помощи свойства End.
Модификаторы доступа l l l Любой вновь создаваемый класс может содержать разделы, определяемые зарезервированными словами published (опубликованные), private (закрытые), protected (защищенные), public (доступные) и automated (автоматизированные). Внутри каждой секции вначале определяются поля, а затем - методы и свойства. Public – нет ограничений на область видимости перечисляемых в ней полей, методов и свойств. Private – доступ только для элементов класса и его потомков, расположенных в данном модуле. Protected - доступ только для методов самого класса, а также любых его потомков, независимо от того, находятся ли они в том же модуле или нет. По умолчанию задается доступ public. В Object Pascal разрешается сколько угодно раз объявлять любую секцию, причем порядок следования секций не имеет значения. Любая секция может быть пустой.
Пример объектной иерархии для объектов типа class {$MODE OBJFPC} // компиляция в режиме совместимости с Object Pascal unit objoop; interface uses Graphics; type TGrob=class private xx, yy: integer; cc: TColor; vv: boolean; can: TCanvas; public constructor create(x 1, y 1: integer; c 1: TColor; can 1: TCanvas); procedure move(dx, dy: integer); procedure show; procedure hide; procedure draw(cl: TColor); virtual; abstract; property x: integer read xx write xx; property y: integer read yy write yy; property color: TColor read cc write cc; end;
Пример объектной иерархии для объектов типа class TPoint=class(TGrob) private public procedure draw(cl: TColor); override; end; TCircle=class(TPoint) private rr: word; public constructor create(x 1, y 1: integer; r 1: word; c 1: TColor; can 1: TCanvas); procedure draw(cl: TColor); override; property r: word read rr write rr; end;
Пример объектной иерархии для объектов типа class implementation {-------------------------} constructor tgrob. create(x 1, y 1: integer; c 1: TColor; can 1: TCanvas); begin inherited create; xx: =x 1; yy: =y 1; cc: =c 1; vv: =false; can: =can 1; end; {-------------------------} procedure tgrob. move(dx, dy: integer); begin draw(clblack); xx: =xx+dx; yy: =yy+dy; draw(cc); end;
Пример объектной иерархии для объектов типа class {-------------------------} procedure tgrob. show; begin vv: =true; draw(cc); end; {-------------------------} procedure tgrob. hide; begin vv: =false; draw(clblack); end; {-------------------------} procedure tpoint. draw(cl: TColor); begin can. pixels[xx, yy]: =cl; end; {-------------------------------------------------} constructor tcircle. create(x 1, y 1: integer; r 1: word; c 1: TColor; can 1: TCanvas); begin inherited create(x 1, y 1, can 1); rr: =r 1; end; {-------------------------} procedure tcircle. draw(cl: TColor); begin can. pen. color: =cl; can. brush. style: =bsclear; can. ellipse(xx-r, yy-r, xx+r, yy+r); end; {-------------------------} begin end.
Пример работы с объектной иерархией l Как несложно заметить реализация объектной иерархии на языке Object Pascal несколько проще реализации на Borland Pascal. const n=100; var Form 1: TForm 1; c: array [1. . n] of tcircle; i, x, y, w, www: integer; r: word; cl: TColor; procedure TForm 1. Bit. Btn 1 Click(Sender: TObject); begin randomize; for i: =1 to n do begin x: =random(paintbox 1. Width); y: =random(paintbox 1. Height); r: =random(30); w: =random(255); www: =random(255); cl: =rgb(w, www); c[i]: =TCircle. create(x, y, r, cl, paintbox 1. canvas); c[i]. show; end;
Пример работы с объектной иерархией procedure TForm 1. Speed. Button 1 Click(Sender: TObject); begin if speedbutton 1. Down=true then timer 1. enabled: =true else timer 1. enabled: =false; end; procedure TForm 1. Timer 1 Timer(Sender: TObject); begin randomize; for i: =1 to n do begin w: =random(255); www: =random(255); c[i]. color: =rgb(ww, w, www); c[i]. move(random(10)-5, random(10)-5); end;
Применение ООП для описания сложных систем l l l l Пусть имеется замкнутое пространство, например, лес квадратной формы. В этом лесу живут организмы трех типов: трава, зайцы и волки. Необходимо смоделировать поведение участников данной экологической системы. Представим участников нашей системы в виде иерархической объектной структуры. В качестве общего предка введем абстрактный объект «Лесной житель» . У данного объекта будут прописаны все характерные свойства наших участников: текущее местоположене в лесу, возраст, здоровье. «Трава» будет потомком «Лесного жителя» , она уже будет иметь визуальное решение. Трава должна иметь возможность умирать от старости и вырастать заново. «Заяц» будет потомком «Травы» . У него появится новое поле – пол. Он сможет передвигаться. При встрече ослабленного (голодного) зайца с травой должно происходить прекращение жизни травы и улучшение самочувствия зайца. При встрече двух разнополых зайцев должен появляться третий.
Применение ООП для описания сложных систем l l l l «Волк» будет потомком «Зайца» он сможет есть зайцев, т. е. отнимать у них здоровье и пополнять свое. В начале работы программы создаются определенные количества каждого типа объектов. Далее включается пошаговая система типа клеточного автомата. За один ход каждый объект может сделать один ход. Этим ходом может быть перемещение, отдых, старение (для травы), размножение или поедание жертвы с целью восстановления сил. Можно ввести дополнительные правила, например, заяц, оказываясь рядом с волком от страха перестает двигаться и следующим ходом волк может его спокойно съесть. Участник системы может умереть, т. е. полностью лишиться своего здоровья вследствие поедания или от старости. Каждому участнику системы выдается свое графическое обозначение, так чтобы визуализация была наглядной и удобной. Суммарные зависимости популяций от времени можно отобразить в виде графиков или диаграмм.