Как известно, существует три основных принципа, составляющих суть объектно-ориентированного программирования: инкапсуляция, наследование и полиморфизм. Классическое правило объектно-ориентированного программирования утверждает, что для обеспечения надежности нежелателен прямой доступ к полям объекта: чтение и изменение их содержимого должно осуществляться посредством вызова соответствующих методов. Это правило называется инкапсуляцией (сокрытие данных). В старых реализациях ООП (например в Turbo Pascal) эта мысль внедрялась только посредством призывов и примеров в документации; в Delphi есть соответствующая конструкция. Пользователь объекта в 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% полей недоступны и заменены базирующимися на них свойствами. Того же правила следует придерживаться и при разработке собственных классов.