Класс TPerson содержит четыре элемента класса – два поля и два метода. И поля, и методы без затруднений доступны из любого объекта:
var Man: TPerson;
...
Man.SetAge(36); // работа с полем через метод
Man.fAge:= -100; // непосредственная работа с полем
Приведённый пример показывает недостаток подобной свободы. Одно из назначений метода TPerson.SetAge – корректная установка возраста. Вместе с тем этот метод можно обойти, установив возраст прямо через поле.
В Object Pascal существуют специальные директивы ограничения видимости, которые при описании класса позволяют контролировать видимость его элементов. Эти директивы разделяют объявление класса на секции видимости.
Рассмотрим две директивы – private и public. Все элементы класса из секции private доступны для использования только в том модуле или программе, которые содержат объявление класса. В секции private обычно размещаются поля и методы, описывающие внутренние особенности реализации класса. Элементы из секции public не имеет ограничений на использование.
Подчеркнём следующие особенности использования директив. Во-первых, внутри модуля с описанием класса директивы ограничения видимости не действуют. Во-вторых, по умолчанию для элементов класса установлена директива видимости public. И, наконец, порядок директив в описании класса произволен, допускается повторение директив. Однако принято описывать элементы класса в порядке увеличения открытости.
|
|
Применим директивы private и public к классу TPerson, поместив его в отдельный модуль. Обратите внимание: описание класса помещено в секцию interface, а реализация – в секцию implementation:
unit TPersonClass;
interface
type TPerson = class
private
fName: string;
fAge: Integer;
public
procedure SetAge(Age: Integer);
function SayName: string;
end;
implementation
procedure TPerson.SetAge(Age: Integer);
begin
if Age > 0 then fAge:= Age
end;
function TPerson.SayName: string;
begin
Result:= 'My name is ' + fName
end;
end.
Теперь попытка обратится к полю fAge в программе, использующей класс TPerson, вызовет ошибку компиляции:
program OOPProgramm;
uses TPPersonClass
var Man: TPerson;
...
Man.fAge:= -100; // ошибка компиляции!
Таким образом, при помощи директив поля класса защищены от «вторжения» пользователей. Однако теперь у TPerson появился недостаток – значения полей нельзя прочитать непосредственно. Этот недостаток устраняется введением в класс дополнительных методов. В общем случае в ООП принято осуществлять доступ к полям только через методы класса. Говорят, что такие методы составляют интерфейс класса.
type TPerson = class
private
fName: string;
fAge: Integer;
public
procedure SetAge(Age: Integer);
function GetAge: Integer;
procedure SetName(Name: string);
function SayName: string;
end;
Обсудим преимущества, которые даёт использование методов для доступа к полям. Первое преимущество: при использовании методов данные объекта могут быть представлены в разных форматах без дублирования. Для иллюстрации рассмотрим простой класс TTemperature, назначение которого – хранить данные о температуре. В классе будет использоваться две пары методов для чтения и записи поля, что позволит получать температуру в двух вариантах – градусах Цельсия и кельвинах:
|
|
type TTemperature = class
private
fTemp: double;
public
procedure SetTempCelsius(Temp: double);
function GetTempCelsius: double;
procedure SetTempKelvin(Temp: double);
function GetTempKelvin: double;
end;
...
procedure TTemperature.SetTempCelsius;
begin
fTemp:= Temp + 273.15
end;
function TTemperature.GetTempCelsius;
begin
Result:= Temp - 273.15
end;
procedure TTemperature.SetTempKelvin;
begin
fTemp:= Temp
end;
function TTemperature.GetTempKelvin;
begin
Result:= Temp
end;
...
var T: TTemperature;
...
T.SetTempCelsius(20); // установили в градусах Цельсия
A:= T.GetTempKelvin; // прочитали в кельвинах
Второе преимущество использования методов для доступа к полям класса: разработчик класса может незаметно для пользователей изменять структуру хранения данных класса. Например, в классе TTemperature мы могли бы хранить температуру в градусах Цельсия. Для этого нам пришлось бы переписать методы класса. Но если оставить их заголовки прежними, пользователь класса подобной замены не увидит.
Подводя итог, можно сформулировать следующее правило: создатель класса должен исключить возможность прямой работы с полями – для этого следует использовать набор интерфейсных методов.