Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

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

..pdf
Скачиваний:
11
Добавлен:
12.11.2023
Размер:
16.61 Mб
Скачать

5.3. Свойства

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

property <имя свойства>:<тип>

[read <метод чтения или имя поля>] [write <метод записи или имя поля>] [stored <метод или булевское значение>] [default <константа>];

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

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

Остальные спецификации, названные спецификациями сохранения, используются только для свойств, определяемых в секции published, т.е. используемых для «опубликованных» компонент. Булевское значение после stored (вместо которого может быть указан метод, возвращающий булевское значение) определяет, должно ли сохраняться данное свойство компонента при сохранении формы (true - должно, false - не должно) в файле с расширением

.dfm. Последний спецификатор default определяет стандартное значение свойства, при совпадении значения поля с которым свойство не сохраняется.

Например:

private FValue:integer;

procedure SetValue(AValue:integer); function StoreValue:boolean;

published

property Value:integer read FValue write SetValue stored StoreValue default 10; . . .

Данное описание определяет свойство для внутреннего поля FValue некоторого класса. Чтение свойства выполняется напрямую из поля. Для записи в поле используется специальный метод SetValue. Сохранение в форме выполняется, если метод StoreValue возвращает true и значение отлично от 10.

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

тем же именем, что и свойство, но с префиксом «F», а соответствующие параметры методов ввода и вывода с префиксом «А». Аналогично, имя метода чтения свойства желательно начинать с префикса «Get», метода записи - с «Set».

211

5. Объектная модель Delphi Pascal

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

В программе свойства используются как обычные поля, найример:

A. Value:=п;

или

k: =А. Value;

соответственно при этом будут выполняться следующие операции:

A.SetValue(n);

и

к:=A.FValue;

Простые свойства целесообразно использовать:

-для ограничения доступа к полю (например, доступ только для чтения);

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

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

Определим в классе два свойства Color и Radius. Изменение значений этих свойств свяжем с перерисовкой окружности. На рис. 5.9 представлена диаграмма класса TMyCircle для этого варианта.

Добавим еще одно изменение: поместим вызов процедуры рисования Draw в конструктор, так как конструирование объекта обычно связано с его рисованием. Текст модуля Circle имеет вид:

TObject

Поля: Image: TImage; х, у, FRadius: Word; FColor: TColor TMyCircle Методы: Create, Draw, Clear, SetSize, SetColor

Свойства: Color: TColor; Radius: Word

Рис. 5.9. Диаграмма класса TMyCircle

212

5.3. Свойства

Unit Circle;

Interface

Uses extctrls,Graphics; Type TMyCircle=class

private x,y,FRadius:Word; FColor: TColor; Image. TImage; Procedure Clear;

Procedure Draw;

Procedure SetSize(ARadius: Word);

Procedure SetColor(AColor:TColor);

public

Constructor Create(almage:TImage;ax,ay,ARadius: Word; AColor:TColor);

property Radius:Word write SetSize; property Color:TColor write SetColor;

end;

 

 

Implementation

 

Constructor TMyCircle.Create;

 

Begin

inherited Create;

 

 

Image: =almage; x:=ax;

y: =ay;

 

FRadius: =ARadius; FColor: =AColor;

End;

Draw;

 

 

 

Procedure TMyCircle.Draw;

 

Begin

Image.Canvas.Pen.Color: =FColor;

 

Image.Canvas.Ellipse(x-FRadius, y-FRadius,

End;

 

x+FRadius, y+FRadius);

 

 

Procedure TMyCircle.Clear;

 

Var TempColor:TColor;

 

Begin

TempColor:=FColor;

 

 

FColor: =Image.Canvas.Brush.Color;

Draw;

FColor:= TempColor;

End;

Procedure TMyCircle.SetSize;

Begin Clear; FRadius:=ARadius; Draw; end;

Procedure TMyCircle.SetColor;

Begin Clear; FColor:=AColor; Draw; end;

End.

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

213

5. Объектная модель Delphi Pascal

С.Radius: =strtoint(rEdit. Text);

или

C.Color: =ColorDialog.Color;

Пр и м e ч а н и e. Именно такой способ программирования лежит в основе всех

стандартных компонент Delphi. Так, например, изменение свойства Visible, определенного для всех визуальных компонент, сопровождается в зависимости от ситуации появлением или

исчезновением соответствующего компонента.

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

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

property <имя> <список индексных параметров>:<тип>

[read <метод чтения>] [write <метод записи>] [default];

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

Вспецификациях доступа должны указываться методы, количество, тип

ипорядок следования параметров которых должны соответствовать списку индексных параметров. Параметр-значение в методе записи при этом указывается в конце списка.

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

Например:

private function GetValMas(I:word;F:double):word; procedure SetValMas(I:word;F:double;AElement:word);

public prorerty ValMas[I:word, F:double]:word

read GetValMas write SetValMas; default;

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

214

5.3. Свойства

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

MyObject[k+2,s] :=1;

эквивалентно

MyObject. ValMas[k+2,s]:=l;

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

Пример 5.4. Использование свойств-массивов (класс Динамический массив» - вариант 1). Пусть требуется разработать класс для реализации динамического массива с произвольным количеством элементов-байт. Максимальный размер массива необходимо определять при его создании. В процессе работы должен отслеживаться реальный размер массива.

Предусмотреть возможность выполнения следующих операций:

-ввод массива из элемента TSringGrid и вывод в такой же элемент;

-модификацию указанного элемента;

-вставку элемента на указанное место;

-удаление элемента с заданным индексом.

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

Unit MasByte;

Interface

Uses SysUtils,Dialogs, Grids;

Type

TMas=array[l..255] o fbyte;

TMasByte = class(TObject) {класс «Динамический массив»}

private

ptr_an: ATMas; {указатель на массив}

len. byte; {максимальная длина массива}

Procedure SetEl(Ind:byte;т:byte); {процедура записи элемента}

Function GetEl(Ind:byte):byte;

{функция чтения элемента}

public

 

 

n.Byte;

{реальный размер массива}

Constructor Create(an:byte);

Destructor Destroy;override;

215

5. Объектная модель Delphi Pascal

Property Mas[Ind: byte]:byte read GetEl write SetEl;default; Procedure Modify(Ind:byte; Value.byte); {модификация элемента} Procedure Insert(Ind:byte; Value. byte); {вставка элемента}

Function Delete(Ind:byte):byte;

{удаление элемента}

Function InputMas(Grid: TStringGrid;I,J:integer):boolean; {ввод}

Procedure OutputMas(Grid:TStringGrid;I,J:integer);

{вывод}

end;

 

 

 

Implementation

 

 

 

Constructor TMasByte.Create;

 

 

Begin

 

 

 

inherited Create;

 

 

 

GetMem(ptr_an,an);

len:=an;

{ n:=0; указывать не надо}

End;

Destructor TMasByte.Destroy; Begin

FreeMem(ptr_an); inherited Destroy;

End;

Procedure TMasByte.SetEl(Ind:byte;m:byte); Begin

iflnd<=len then

if Ind<=n thenptr_an*flnd]:=m else

MessageDlgfBм а с с и в е H e m '+ in tto str(In d )+

'-го элемента. ',mtError,[mbOk],0)

else

MessageDlg('B массиве можно разместить только'+ inttostr(Len) + ' элементов.',mtError,[mbOk],0);

End;

Function TMasByte.GetEl(Ind:byte).byte; Begin

I f Ind<=n then Result: =ptr_anA[Ind/ else

MessageDlgC В массиве нет '+inttostr(Ind)+

'-го элемента.',mtError,[mbOk],0);

End;

Function TMasByte.InputMas(Grid:TStringGrid;I,J:integer).boolean; Var k:byte; x,er_code:integer;

Begin

with Grid do begin

k:=0; Result: =true;

216

5.3. Свойства

while (Cells[k+I,JJo")and Result do begin

Val(Cells[k+I,JJ,x,er_code); if er_code-0 then

ifx<=255 then Insert(k+1,x) else

begin

MessageDlgC Значение не может превышать 255. mtError,[mbOk\0);

Result:=false;

Exit; end

else begin

MessageDlgf'B строке обнаружены недопустимые символы.mtError, [mbOk\,0);

Result: =false; Exit;

end;

k:=k+l;

end;

OutputMas(Grid,I,J);

end;

End;

Procedure TMasByte.OutputMas(Grid:TStringGrid;I,J:integer); Var k. byte;

Begin

with Grid do begin

if n+I>ColCount then ColCount: =n+I; for k: =0 to ColCount-1 do

ifk<n then Cells[I+k,J]: =inttostr(mas[k+1]) else Cells[I+k,J]: =”;

end;

End;

Procedure TMasByte.Modify;

Begin Mas[Ind\:=Value; End; Procedure TMasByte.Insert;

Var i:integer; Begin

n:=n+l; for i:=n-l downto Inddo Mas[i+1]:-Mas[i]; Mas[Ind\:= Value;

End;

217

5. Объектная модель Delphi Pascal

Function TMasByte.Delete; Var i:integer;

Begin

Result: =Mas[Ind];

for i:=Ind+1 ton do Mas[i-1]: -Mas[i\; n: -n-1; End;

End.

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

A: =TMasByte.Create(k);

после чего для обращения к элементам достаточно указать имя объекта и индекс, начиная с 1:

А[6]:=7;

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

И ндексируем ы е свойства (свойства со специф икацией index). Описание индексируемых свойств выполняется следующим образом:

property <имя> : <тип> index <константа>

read <метод чтения> w rite <метод записи>;

где <константа> —целое число типа Shortlnt.

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

массива:

 

Type CMas=class

 

private

 

FMasEl:array[l:3] o fword;

{поле-массив}

function GetEl(Ind:byte). word;

{метод чтения}

procedure SetEl(Ind:byte;AElement); {метод записи} public

218

5.4. Метаклассы

property Elementl:word index 1 read GetEl write SetEl; property Element2:word index 2 read GetEl write SetEl; property Element3:word index 3 read GetEl write SetEl;

end;

Function CMas. GetEl;

Begin Result: =FMasEl[Ind\; End; Procedure CMas. SetEl;

Begin FMasEl[Ind\:=AElement; End;

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

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

5.4. Метаклассы

Delphi содержит достаточно богатый арсенал средств создания и обработки полиморфных объектов. Так в дополнение к заложенной еще в Borland Pascal 7.0 совместимости указателей на базовый и производные классы имеется возможность определения специальных переменных {метаклассов), значением которых являются классы. Имеются также специальные операции проверки принадлежности объекта некоторой иерархии классов {is) и уточнения класса заданного объекта {as).

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

Туре <имя типа ccbuiKH>=class of <базовый класс ссылки>; Var <имя переменной>:<имя типа ссылки>;

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

В Delphi Pascal стандартно определена ссылка типа TClass, базовым классом которой является класс TObject. Переменной типа TClass можно присвоить имя любого класса Delphi, так как все они наследуются от класса TObject:

Type TClass=class o f TObject;

Var c. TClass; d:TObject;...

c:=TButton;

d:=c.Create{...) ...

219

J. Объектная модель Delphi Pascal

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

<имяобъекта> is <имя класса>

Операция возвращает true, если объект принадлежит классу, и false - в противном случае.

Операция as. Эта операция используется в тех случаях, когда тип объекта может отличаться от типа используемой переменной для обеспечения доступа к «невидимым» (см. рис. 1.25) полям и методам:

<имяобъекта> as <имякласса>

Вотличие от простого переопределения типа, использующего имя типа

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

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

методами класса.

Методы класса описываются со спецификатором class:

Type M yClass=class(TObject) public

class procedure <имя метода>(<список параметров:»);

end;

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

Механизм определения типов на этапе выполнения программы . Все перечисленные выше возможности непосредственно обеспечиваю тся реализованным в Delphi механизмом определения типов на этапе выполнения.

Программа на Delphi Pascal содержит специальные таблицы, в которых хранится информация об используемых типах данных. Эта информация (КГП - Run Time Type Information — «информация о типе времени выполнения») доступна во время выполнения программы. Такая информация хранится для типов, которые можно использовать в опубликованных свойствах (для целых и вещественных чисел, символов, перечислений, строк, множеств, классов,

220