Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Листинг 5.5. Задание областей видимости. Первый подход




Unit UsingPrivatel; {Заголовок модуля}

Interface {Начало интерфейсной части}

Туре

ABC = class {Заголовок класса ABC}

private {Начало области видимости private)

a, b: Double; (Свойства а и b имеют узкую область

видимости}

public {Начало области видимости public}

с: Double; {Свойство с имеет широкую область

видимости}

Procedure SetA(NewA: Double);

{Метод SetK имеет широкую область видимости}

Procedure SetB(NewB: Double);

{Метод SetB имеет широкую область видимости}

end;

Implementation {Начало описательной части}

Procedure ABC.SetA(NewA: Double);

{Описание метода SetA класса А В С}

Begin

а: = NewA; {Установка значения свойства а.]

с:= а * Ь; {Обновление значения свойства c-

поддержание целостности данных}

end;

Procedure ABC.SetB(NewB: Double);

{Описание метода SetB класса ABC

Begin

b:= NewB; {Установка значения свойства b}

с:= a * b; {Обновление значения свойства с -

поддержание целостности данных}

end;

end. {Окончание модуля}

 

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

 

Листинг 5.6. Задание областей видимости. Второй подход

Unit UsingPrivate2; {Заголовок модуля}

 

Interface {Начало интерфейсной части}

 

Type

ABC2 = class {Заголовок класса ABC}

Private {Начало области видимости private}

с: Double; {Свойство с имеет узкую область видимости}

Public {Начало области видимости public}

a, b: Double; {Свойства а и b имеют широкую область видимости}

Function GetC: Double; {Метод GetC имеет широкую область видимости}

end;

 

Implementation {Начало описательной части}

Function ABC2.GetC:Double;

{Описание метода GetC класса АВС2}

Begin

с:= а*b; {Обновляем значение свойства с}

Result:= с { Возвращаем значение с вызывающей подпрограмме}

end;

end. {Окончание модуля}

 

При такой конструкции класса целостность данных не может быть нарушена вызывающими подпрограммами, если только они не находятся в одном модуле с описываемым классом, то есть, в данном случае, в модуле UsingPrivate2. Единственным недостатком такого подхода является постоянный пересчет значения с при каждом обращении к методу GetC. Поэтому наиболее оптимальным вариантом решения нашей задачи будет сокрытие всех свойств класса и реализация методов доступа к ним через методы (см. листинг 5.7).

 

Листинг 5.7. Задание областей видимости. Третий подход (оптимальный)

unit UsingPrivate3; {Заголовок модуля}

Interface

Type

АВСЗ = class

Private

a, b, c: Double; {Все свойства имеют узкую область

видимости}

Public

Procedure SetA(NewA: Double);

{Все методы имеют широкую область видимости}

Procedure SetB(NewB: Double);

Function GetC: Double; end;

Implementation

Procedure ABC3.SetA(NewA: Double);

{Описание метода SetA класса АВСЗ}

Begin

a: = NewA;

с:= a * b;

end;

Procedure АВСЗ.SetB(NewB: Double);

{Описание метода SetB класса АВСЗ}

Begin

b:= NewB;

с:=. a * b;

end;

Function ABC3.GetC: Double;

{Описание метода GetC класса АВСЗ}

Begin

Result:= с; {Просто возвращаем значение с}

end;

 

End.

 

Property-свойства

 

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

 

Итак, описание свойств property в простейшем виде выглядит следующим образом:

property <Имя свойства>: <Тип> read <Функция чтения значения>

write <Процедура установки значения>;

 

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

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

Пример описания и использования property-свойства для поддержания корректности данных приведен в листинге 5.8.

Листинг 5.8. Пример описания и использования property-свойства

Unit Properties; {Заголовок модуля}

Interface

Type

ABC4 = class

Private

fa, fb, fc: Double; {Все свойства имеют узкую область видимости}

protected {Все методы имеют область видимости protected}

Procedure SetA(NewA: Double); {Процедура установки значения свойства fa}

Procedure SetB(NewB: Double); {Процедура установки значения свойства fb}

 

Function GetA: Double; {Функция получения значения свойства fa}

Function GetB: Double; {Функция получения значения свойства fb}

Function GetC: Double; {Функция получения значения свойства fc}

Published {Раздел описания property-свойств, доступных вызывающему фрагменту программы}

property A:Double read GetA write SetA;

{Описание property-свойства А, для которого задана функция чтения GetA и процедура установки SetA. Свойство будет доступно вызывающему фрагменту программы для чтения и записи}

property B:Double read GetB write SetB;

{Описание property-свойства В, для которого задана функция чтения GetB и процедура установки SetB. Свойство будет доступно вызывающему фрагменту программы для чтения и записи}

property C:Double read GetC;

{Описание property-свойства В, для которого задана функция чтения GetC, но не задана процедура установки. Свойство будет доступно вызывающему фрагменту программы только для чтения}

end;

 

Implementation

Procedure ABC4.SetA(NewA: Double); {Описание метода SetA}

Begin

fa:= NewA; {Занесение нового значения в private-свойство fa}

fc:= fa * fb; {Пересчет произведения и занесение результата в свойство fc}.

end;

Procedure ABC4.SetB(NewB: Double); {Описание метода SetB}

Begin

fb:= NewB; {Занесение нового значения в private-свойство fb}

fc:= fa * fb; {Пересчет произведения и занесение результата в свойство fc}

end;

Function ABC4.GetA: Double; {Описание метода GetA}

Begin

Result:= fa; {Результат функции — значение private-свойства fa}

end;

Function ABC4.GetB: Double; {Описание метода GetB}

Begin

Result:= fb; {Результат функции — значение private-свойства fb}

end;

Function ABC4.GetC: Double; {Описание метода GetC}

Begin

Result:= fc; {Результат функции — значение private-свойства fc}

end;

end. {Окончание модуля}

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

Заметим, что вместо функции чтения и процедуры установки может, быть указано одно из обычных свойств, вне зависимости от его области видимости. В рассмотренном примере такую возможность следует применить при описании property-свойств а, b и с, указав вместо функций чтения свойства fa, fb и fc, соответственно. Модифицированный текст модуля приведен в листинге 5.9.

Листинг 5.9. Пример описания и использования property-свойства (модифицированный)

Unit Properties; {Заголовок модуля}

Interface

Type

ABC4 = class

Private

fa, fb, fc: Double; {Все свойства имеют узкую область видимости}

protected {Все методы имеют область видимости protected}

Procedure SetA(NewA: Double); {Процедура установки значения свойства fa}

Procedure SetB(NewB: Double); {Процедура установки значения свойства fb}

Published {Раздел описания property-свойств, доступных вызывающему фрагменту программы}

property A:Double read fa write SetA; {Для установки значения property-свойства используется метод SetA, а для чтения private-свойство fa}

property B:Double read fb write SetB;

{Для установки значения property-свойства используется метод SetB, а для чтения private-свойство fb}

property С:Double read fc;

{Для чтения property-свойства используется значение private-свойства fc}

end;

Implementation

Procedure ABC4.SetA(NewA: Double); {Описание метода SetA}

Begin

fa:= NewA;

fc:= fa * fb;

end;

Procedure ABC4.SetB(NewB: Double); {Описание метода SetB}

Begin

fb:= NewB;

fc:= fa * fb;

end;

end. {Окончание модуля}

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

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

 

5.2.1. Основы наследования

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

При описании дочернего класса с использованием наследования имя родительского класса указывается в скобках после ключевого слова с1ass| в заголовке интерфейсной части описываемого класса:

Туре

<Имя класса> = class (<Имя родительского класса>) {Заголовок описания}

......... {Описание собственных свойств и методов}

End;

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

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

 

5.2.2. Переопределение методов

Часто встречается ситуация, когда один или несколько методов дочернего класса должны работать не так, как в родительском классе. В качестве примера можно привести класс TGeomFigure, представляющий собой абстрактную геометрическую фигуру, и имеющий метод Draw для ее рисования. Наследниками этого класса могли бы быть классы TCircle для работы с окружностями, TLine для работы с линиями, и так далее.

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

Механизм переопределения

 

Для переопределения метода, реализованного в объекте-родителе, следует:

 

♦ указать его заголовок в интерфейсной части описания дочернего класса без изменения имени, списка параметров и возвращаемого значения (если метод является функцией);

♦ указать после заголовка метода ключевое слово override. Если ключевое слово override не указано, то метод не переопределяется.

♦ реализовать метод (создать программный код) в описательной части объекта по обычным правилам. При этом в заголовке метода ключевое слово override не указывается.

 

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

Разница между использованием virtual и dynamic заключается в направлении оптимизации компилятором вызовов переопределяемых методов. Методы, помеченные virtual, оптимизируются по скорости, а zynamic-методы по размеру программного кода. В большинстве случаев рекомендуется использование виртуальных методов, а использование динамических методов целесообразно при высоких степенях вложенности cвязей родитель-наследник.

Приведем описание класса TwoNums с двумя свойствами а и Ь, и методом GetResult, возвращающим сумму свойств. Далее, от этого класса эпишем наследника ThreeNums, имеющего уже три свойства — а, b и с, и переопределяющего метод GetResult таким образом, чтобы возвра­щать сумму не двух, а трех чисел (листинг 5.10).

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...