Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ВВЕДЕНИЕ В ОБЪЕКТНО Ориентированное программиро...docx
Скачиваний:
19
Добавлен:
29.08.2019
Размер:
1.01 Mб
Скачать

При использовании динамики:

procedure InStack(var TopStack:TTopStack, Elem:TElem);

function OutStack(var TopStack:TTopStack, var Elem: TElem) :boolean;

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

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

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

.

Описание объекта также не является полным.

Использование:

Var

DSStack:TDObStack;

SStack:TSObStack;

. . .

A:TElem;

Begin

. . .

DStack.InStack(a);

SStack.InStack(a);

. . .

if not (DStack.OutStack(a)) writeln(‘Динамический стек пуст’);

if not (SStack.OutStack(a)) writeln(‘Статический стек пуст’);

. . .

End;

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

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

Type

TAnObject = class(TObject)

Function GetAProperty: TSomeType;

procedure SetAProperty(ANewValue: TSomeType);

property AProperty:TSomeType read GetAProperty write SetAProperty;

end;

Функция, имя которой указывается после директивы read служит для чтения значения свойства. Она не имеет параметров, тип возвращаемого значения совпадает с типом свойства. Процедура, указанная после директивы write служит для установки значения свойства. Она имеет единственный параметр, чей тип совпадает с типом свойства. В приведенном примере доступ к значению свойства AProperty осуществляется через вызов методов GetAProperty и SetAProperty, Однако в обращении к этим методам в явном виде нет необходимости, достаточно написать:

AnObject.AProperty := AValue;

AVariable := AnObject. AProperty;

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

AnObject.SetAProperty(AValue);

AVariable := AnObject.GetAProperty;

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

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

TScreenElem = class (TOblect)

FColor:TColor;

procedure Paint(C:TColor);

function Correct(C:TColor):Boolean;

procedure SetColor (NewColor:TColor);

property Color:TColor read FColor write SetColor;

end;

. . .

procedure TScreenElem. SetColor (NewColor. - TColor) ;

Begin

if (NewCoIor=FColor) and Correct (NewColor) then

begin

PColor:=NewColor;

Paint(NewColor) ;

end;

End;

В этом примере чтение значения свойства цвета экранного элемента Color означает просто чтение поля FColor. Зато при присвоении значения внутри SetColor вызывается сразу два метода: проверки корректности заданного значения цвета Correct и перерисовки экранного элемента Paint.

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

Type

TanObject =class(TObject)

property AProperty:TSomeType read GetValue;

end;

В этом примере вне объекта значение свойства можно лишь прочитать; попытка присвоить свойству AProperty значение вызовет ошибку компиляции.

Для присвоения свойству значения по умолчанию используется ключевое слово default:

Property Visible: boolean read FVisible write SetVisible default True;

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

property APoints [ Index : Integer ] :TPoint read GetPoint write SetPoint;

property Pixels [X, Y: Integer]: TColor read GetPixel write SetPixel;

property Values [const Name: string]: string read GetValue write SetValue;

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

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

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

function GetPoint (Index -Integer) :TPoint;

function GetFixel (X, Y: Integer): TColor;

function GetValue (const Name: string): string;

Аналогично, метод, помещающий значения в такое свойство, должен первыми параметрами иметь индексы, а далее - переменную нужного типа (которая может передаваться как по ссылке, так и по значению):

procedure SetPoint (Index: integer; NewPoint :TPoint) ;

procedure SetPixel(X, Y: Integer; Value: TColor);

procedure SetValue (const Name, Value: string);

Доступ к векторным свойствам осуществляется с помощью индексов. Например:

if Collection. Points[0] = constPoint then Exit;

Canvas. Pixels[10, 20] := clRed;

Params. Values ['PATH'] := 'C:DELPHIBIN';

Или путем вызова методов:

if Collection .GetPoint (0) = constPoint then Exit;

Canvas. SetPixel (10, 20, clRed) ;

Params. SetValuef (‘PATH’, 'C:DELPHIBIN') ;

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

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

Type

TMyObject = class;

property Strings [ Index : Integer ]: string read Get write Put; default;

end;

var

AMyObject : TMyObject;

begin

. . .

AMyObject. Strings [1] := 'First method'; {первый способ}

AMyObject [2] := ‘Second method’; {второй способ}

End

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

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

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