Реферат: Программирование на Delphi
Обычно свойство определяется тремя элементами: полем и двумя методами осуществляющими его чтение/запись:
type
tMyClass=class(tObject)
function GetaProperty: tSomeType;
procedure SetaProperty(Value: tSomeType);
property aProperty: tSomeType read GetaProperty
write SetaProperty;
end;
В данном примере доступ к значению свойства aProperty осуществляется через вызовы методов GetaProperty и SetaProperty, однако в обращении к этим методам в явном виде нет необходимости: достаточно написать
aMyObject.aProperty:=aValue;
aVarable:= aMyObject.aProperty;
и Delphi откомпилирует эти операторы в вызовы соответствующих методов. То есть внешне свойство выглядит в точности как обычное поле, но за всяким обращением к нему могут стоять вызовы необходимых программисту методов. Например, если есть объект, представляющий собой квадрат на экране, и его свойству “цвет” присваивается значение “белый”, то произойдет немедленная прорисовка, приводящая реальный цвет на экране в соответствие значению свойства.
В методах, устанавливающих значения свойства, может производиться проверка значения на попадание в заданный диапазон значений и вызов других процедур зависящих от вносимых изменений. Если же потребности в специальных процедурах чтения/записи нет, можно вместо имен методов применять имена полей.
tPropClass=class
fValue: tSomeType;
procedure SetValue(aValue: tSomeType);
property Value:tSomeType read fValue write SetValue;
End;
В этом примере поле fValue модифицируется при помощи метода SetValue, а читается напрямую.
Если свойство должно только читаться или только записываться, в его описании может присутствовать только соответствующий метод:
tReadOnlyClass=class
property NotChanged:tSomeType read GetNotChanged;
End;
В этом примере свойство доступно только для чтения. Попытка присвоить значение свойству NotChanged вызовет ошибку компиляции.
Свойствам можно присваивать значения по умолчанию. Для этого служит ключевое слово default:
Property Visible:boolean read fVisible write SetVisible default TRUE;
Это означает, что при запуске программы свойство будет установлено компилятором в TRUE.
Свойство может быть и векторным. В этом случае оно выглядит как массив:
Property Points[index:integer]:tPoint read GetPoint write SetPoint;
Для векторного свойства необходимо описать не только тип элементов массива, но также и тип индекса. После ключевых слов read и write должны идти имена соответствующих методов. Использование здесь полей массивов недопустимо. Метод, читающий значение векторного свойства, должен быть описан как функция, возвращающая значение того же типа, что и элементы свойства, и имеющая единственный параметр того же типа и с тем же именем, что и индекс свойства:
function GetPoint(index:integer):tPoint;
Аналогично, метод, помещающий значения в такое свойство, должен первым параметром иметь индекс, а вторым - переменную нужного типа.
procedure SetPoint(index:integer; Value:tPoint);
У векторных свойств есть еще одна важная особенность: некоторые классы в Delphi (списки tList, наборы строк tStrings и т.д.) “построены” вокруг одного основного векторного свойства. Основной метод такого класса дает доступ к элементам некоторого массива, а все основные методы являются как бы вспомогательными. Для упрощения работы с объектами подобного класса можно описать подобное свойство с ключевым словом default:
type tMyList=class
property list[Index:integer]:string read Getlist write Setlist; default;
end;
Если у объекта есть такое свойство, его можно не упоминать, а ставить индекс в квадратных скобках сразу после имени объекта:
var MyList:tMyList
Begin
MyList.list[1]:=’First’; {Первый способ}
MyList.[2]:=’Second’; {Первый способ}
End;
Употребляя ключевое слово default необходимо соблюдать осторожность, т.к. для обычных и векторных свойств оно употребляется в разных значениях.
О роли свойств в Delphi красноречиво говорит тот факт, что у всех имеющихся в распоряжении программиста стандартных классов 100% полей недоступны и заменены базирующимися на них свойствами. Того же правила следует придерживаться и при разработке собственных классов.
Наследование
Вторым “столпом” ООП является наследование. Этот простой принцип означает, что если необходимо создать новый класс, лишь немного отличающийся от уже имеющегося, нет необходимости в переписывании заново уже существующего кода. Вы объявляете, что новый класс
tNewClass=class(tOldClass);
является потомком или дочерним классом класса tOldClass, называемого предком или родительским классом, и добавляете к нему новые поля методы и свойства.
В Delphi все классы являются потомками класса tObject. Поэтому, если вы строите дочерний класс прямо от tObject, то в определении его можно не упоминать. Следующие два описания одинаково верны:
tMyClass=class(tObject);
tMyClass=class;
Более подробно класс tObject будет рассмотрен ниже.
Унаследованные от класса-предка поля и методы доступны в дочернем классе; если имеет место совпадение имен методов, говорят, что они перекрываются.
Рассмотрим поведение методов при наследовании. По тому, какие действия происходят при вызове, методы делятся на три группы. В первую группу отнесем статические методы, во вторую - виртуальные (virtual) и динамические (dynamic) и, наконец, в третью - появившиеся только в Delphi 4 перегружаемые (overload) методы.
Статические методы, а также любые поля в классах-потомках ведут себя одинаково: можно без ограничений перекрывать старые имена и при этом менять тип методов. Код нового статического метода полностью перекрывает (заменяет собой) код старого метода:
type
tFirstClass=class
fData:Extended;
procedure SetData(aValue:Extended);
end;
tSecondClass=class(tFirstClass)
fData:Integer;
procedure SetData(aValue:Integer);
end;
procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1.0;
End;
procedure tFirstClass.SetData(aValue:Extended);
Begin
fData:=1;
inherited SetData(0.99);
End;
В этом примере разные методы с именем SetData присваивают значение разным полям с именем fData. Перекрытое (одноименное) поле предка недоступно в потомке. Поэтому два одноименных поля с именем fData приведены только для примера.
В отличие от поля, внутри других методов перекрытый метод доступен при указании ключевого слова inherited. По умолчанию методы объектов классов статические - их адрес определяется еще на этапе компиляции проекта, поэтому они вызываются быстрее всего.
Принципиально отличаются от статических виртуальные и динамические методы. Они должны быть объявлены путем добавления соответствующей директивы dynamic или virtual. С точки зрения наследования методы этих двух категорий одинаковы: они могут быть перекрыты в дочернем классе только одноименными методоми, имеющими тот же тип.
Полиморфизм. Виртуальные и динамические методы
Рассмотрим следующий пример. Пусть имеется некое обобщенное поле для хранения данных - класс tFiled и три его потомка - для хранения строк, целых и вещественных чисел:
type
tFiled = class
function GetData:string; virtual; abctract;
end;
tStringFiled = class(tFiled)
fData:string;
function GetData: string; override;
end;
tIntegerFiled = class(tFiled)
fData:Integer;
function GetData: string; override;
end;
tExtendedFiled = class(tFiled)
fData:Extended;
function GetData: string; override;
end;
function tStringFiled.GetData: string;
Begin
Result:=fData;
End;
function tIntegerFiled.GetData: string;
Begin
Result:=IntToStr(fData);
End;
function tExtendedFiled.GetData: string;
Begin
Result:=FloatToStr(fData,ffFixed, 7, 2);
End;
function ShowData(aFiled:tFiled): string;
Begin
Form1.Label1.Caption:=aFiled.GetData;
End;