Site icon Обучение программированию с нуля

Области видимости элементов класса

Delphi 7. Занятие 2_7. Часть 4.

Задачи занятия:

  1. понятие инкапсуляции данных и методов в классе;
  2. зоны видимости из различных секций класса;
  3. секция класса PUBLISHED;
  4. секция класса PRIVATE;
  5. свойства класса (property);
  6. примеры доступа к данным и методам из разных секций;
  7. примеры доступа к данным через свойства.

Инкапсуляция данных и методов.

До сих пор мы создавали объекты, значения полей которых изменялись напрямую — через экземпляр класса.

Но одна из основных концепций построения класса — это непрямое взаимодействие с полями.

В механизме создания классов реализуется медицинский принцип — не навреди. Вообще строение класса напоминает айсберг — девять десятых его полей и методов скрыты «под водой» от пользователя.

Пользователь имеет доступ только к тем методам, которые определяют функциональность класса.

С полями ещё интереснее. Как правило, доступ к полям напрямую через экземпляр класса закрыт. Их значения изменяются или считываются через специальный механизм, называемый «свойство класса».

Принцип ограничения доступа к данным и методам класса называется инкапсуляцией.

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

Зоны видимости.

В классе имеются следующие секции, регулирующие степень доступности находящихся там элементов.

  1. Секция «public» (общедоступный). К элементам этой секции можно обращаться из любого модуля. Доступ возможен из методов этого класса, его потомков и вообще из любых процедур.
  2. Секция «published» (публичный). К элементам этой секции можно обращаться из любого модуля. Особенность — элементы этой секции видны в инспекторе объектов (но для этого класс надо зарегистрировать в среде delphi как компонент; а это отдельная тема для разговора). Доступ возможен из методов этого класса, его потомков и вообще из любых процедур. Обычно эта секция идет первой в классе и её название опускается.
  3. Секция «protected» (защищённый). К элементам этой секции можно обратиться из модуля, в котором описан класс. В другом модуле доступ возможен из методов «дружественного» класса или его потомков.
  4. Секция «private» (частный). К элементам этой секции обратиться из других модулей нельзя. Доступ возможен из методов этого же класса, его потомков и процедур, расположенных в данном модуле.
  5. Секция «automated» (элементы так называемой автоматизации). К элементам этой секции можно обращаться из любого модуля. Элементы секции добавляются к интерфейсу OLE-объектов Автоматизации.

Замечание. Естественно, чтобы класс был доступен в другом модуле, его описание должно располагаться в секции interface. Секции могут повторяться в описании класса произвольное количество раз и в произвольном порядке.

Пример 1

Секция класса PUBLISHED

Создадим программу из 2-х форм. В первом модуле Unit1 в секции interface расположим описание класса.

Interface

TYPE

TmyClassA=class //если класс создаётся от Tobject, то Tobject можно не указывать;

//здесь мы явно не указали наименование секции класса; по умолчанию это секция published;

ii:integer;

function fA(i:integer):integer;

constructor create(k:integer=10);

end;

var

Form1: TForm1;

implementation

uses Unit2; //не забываем сделать ссылку на второй модуль, чтобы можно было открыть вторую форму;

{$R *.dfm}

//реализация конструктора

constructor TmyClassA.create(k:integer=10); //конструктор имеет параметр по умолчанию;

begin

ii:=k;

end;

function TMyClassA.fA(i:integer):integer;

begin

result:=i*2;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

unit2.Form2.Show; //отображаем вторую форму

end;

Во второй форме:

implementation

uses Unit1; //ссылка на первый модуль; иначе мы не увидим его секцию interface.

{$R *.dfm}

procedure pr;forward; //опережающее описание, так как сама процедура расположено по тексту ниже, чем её вызов.

procedure TForm2.Button1Click(Sender: TObject);

var xA:TMyClassA;xx,yy:integer;

begin

xA:=TMyClassA.Create; //используем вызов конструктора, используется его значение по умолчанию;

xx:=xA.ii;

yy:=xA.fA(11);

form2.Edit1.Text:=intToStr(xx);

form2.Edit2.Text:=intToStr(yy);

pr; //вызов процедуры

end;

procedure pr;

var xA:TMyClassA;xx,yy:integer;

begin

xA:=TMyClassA.Create(15); //передаём значение через конструктор

yy:=xA.fA(17);

xx:=xA.ii;

form2.Edit3.Text:=intToStr(xx);

form2.Edit4.Text:=intToStr(yy);

end;

В результате имеем:

  1. значение конструктора по умолчанию;
  2. результат работы функции fA;
  3. в конструктор передано значение «15»;
  4. вызов функции из обычной процедуры.

Видимость методов и полей, объявленных в секции public, аналогична (за исключение отображения в инспекторе объектов).

Секция класса PRIVATE

Дополним описание класса:

TYPE

TMyClassA=class

ii:integer;

function fA(i:integer):integer;

function fB(s:string=»):string;

constructor create(k:integer=10);

private //добавили секцию «private»

ss:string;

function fC(s:string):string;

end; //конец описания класса

TMyClassB=class(TMyClassA)

jj:integer;

end;

//метод fA

function TMyClassA.fA(i:integer):integer;

begin

result:=i*2;

end;

//метод fB

function TMyClassA.fB(s:string=»):string;

begin

result:=’Вызов метода fC из метода fB. ‘+#13#10+ fC(‘private’); //вызов private метода fC из published метода fB.

end;

//метод fC

function TmyClassA.fC(s:string):string; //private метод

begin

ss:=’функция fC расположена в секции: ‘+s; //поле ss описано в private секции класса

result:=ss;

end;

Обращаемся к классу из Unit1.

procedure prC(s:string);

var ss:string; sAA:TMyClassA;

begin

sAA:=TMyClassA.create();

ss:=sAA.fC(‘private секция. Вызов из обычной процедуры.’); //вызов метода fC, расположеного в private секции. Метод доступен из процедуры.

form1.memo1.Clear;

form1.Memo1.Text:=ss;

xB:=TMyClassB.create;

form1.Memo2.Clear;

form1.Memo2.Text:=xB.fC(‘private»‘+#13#10+’метод вызван через экземпляр класса-потомка TMyClassB’);

end;

procedure TForm1.Button2Click(Sender: TObject);

var s:string; sA:TMyClassA;

begin

sA:=TMyClassA.create();

s:=sA.fC(‘private.’); //вызов private метода fC. //метод доступен из обработчика события

form1.memo1.Clear;

form1.memo1.Text:=s;

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

prC(‘private’); //вызываем процедуру

//

end;

procedure TForm1.Button4Click(Sender: TObject);

var s:string; sA:TMyClassA;

begin

sA:=TMyClassA.create();

s:=sA.fB; //метод fB доступен из обработчика события

form1.memo1.Clear;

form1.memo1.Text:=s;

//

end;

Метод fB объявлен в секции published. Он доступен отовсюду и в модуле Unit1, и в модуле Unit2.

Доступ возможен из обработчиков событий компонент формы, из методов класса, из методов класса-потомка (класс TMyClassB ) и из обычных процедур.

Метод fC объявлен в секции private. Он доступен отовсюду в модуле Unit1, но в модуле Unit2 доступа к нему нет (на рисунке процедура pr расположена в модуле Unit2):

Использование секции PROTECTED класса

Методы этой секции доступны из модуля Unit1, но не видны в модуле Unit2.

Однако есть такое понятие, как дружественный класс. Если в Unit2 объявить:

TmyClassZ=class(TmyClassA);

то секция protected становится видна для экземпляров этого класса!

Сведём полученные сведения в таблицу:

Для Unit1:

В нашем случае дружественный класс — TmyClassZ=class(TmyClassA);

Как правило, описания классов размещают в отдельном модуле, что обеспечивает недоступность экземпляров объектов к секциям private и protected.

Лазейка остаётся только для дружественных классов для методов, расположенных в секции protected.

Свойства класса.

До сих пор мы обращались к полям класса напрямую. Хотя в принципе это допустимо, но никто так не делает.

В классах обращение к значению полей осуществляется через специальный механизм — свойства класса.

Вот пример такого обращения

в Unit1

Unit1

TYPE

TMyClassX=class

private

poleInt:integer;

procedure setProp(Value:integer);

function GetProp:integer;

public

property IntegerValueA:Integer read poleInt write poleInt;

property IntegerValueB:Integer read poleInt write setProp;

property IntegerValueC:Integer read GetProp write setProp;

end;

function TMyClassX.getProp:integer;

begin

if poleInt<=0 then

showmessage(‘Внимание! значение не положительное’); //showmessage — модальное окно сообщения.

result:=poleInt;

end;

procedure TMyClassX.setProp(Value:integer);

begin

if not (Value=0) then poleInt:=Value

else

showmessage(‘Нулевое значение недопустимо.Введите ещё раз’);

end;

в Unit2

procedure TForm2.Button1Click(Sender: TObject);

var vX:TMyClassX; i:integer;

begin

vX:=TMyClassX.Create;

vX.IntegerValueA:=form2.SpinEdit1.Value;

i:=vX.IntegerValueA;

form2.Edit1.Text:=intToStr(i);

end;

procedure TForm2.Button2Click(Sender: TObject);

//проверяем, не вводим ли мы нулевое значение (отрицательное значение допускается)

var vX:TMyClassX; i:integer;

begin

vX:=TMyClassX.Create;

vX.IntegerValueB:=form2.SpinEdit1.Value; //записываем значение поля через свойство

i:=vX.IntegerValueB;//читаем поле напрямую

form2.Edit1.Text:=intToStr(i);

end;

procedure TForm2.Button3Click(Sender: TObject);

//проверяем, не вводим ли мы нулевое значение

var vX:TMyClassX; i:integer;

begin

vX:=TMyClassX.Create;

vX.IntegerValueC:=form2.SpinEdit1.Value; //записываем значение поля через свойство

i:=vX.IntegerValueC;//читаем поле через свойство.

//Проверка чтобы поле было больше нуля.

form2.Edit1.Text:=intToStr(i);

end;

Рассмотрим класс TmyClassX

В нём объявлены две секции. Первая секция private. Поля и методы этой секции недоступны в Unit2.

Вторая секция — public. Расположенные в неё методы и поля доступны изо всех модулей.

В секции private объявлено поле poleInt:integer; и два метода:

procedure setProp(Value:integer);

function GetProp:integer;

Эти методы обслуживают конструкцию, называемую «свойство».

Свойство выглядит следующим образом:

property имя_свойства:тип read имя_поля разрешение write имя_поля;

или:

property имя_свойства:тип read имя_метода_для_чтения write имя_метода_для_записи;

Возможны также вариации.

Приведём пример:

public

property IntegerValueA:Integer read poleInt write poleInt;

property IntegerValueB:Integer read poleInt write setProp;

property IntegerValueC:Integer read GetProp write setProp;

setProp всегда процедура строго типа procedure имя_процедуры(Value:тип);

GetProp —всегда функция строго вида function имя_функции:тип;

Если отсутствует ключевое слово read, то свойство нельзя прочитать. Если отсутствует ключевое слово write, то свойство нельзя записать.

Примеры функции и процедуры даны выше.

Свойство работает следующим образом.

Если мы хотим прочитать значение поля:

vX:=TMyClassX.Create;

i:=vX.IntegerValueA;//читаем поле напрямую

Если хотим записать значение поля.

vX.IntegerValueA:=form2.SpinEdit1.Value; //записываем значение поля напрямую.

i:=vX.IntegerValueС;//читаем поле через функцию GetProp, которая вызывается неявно.

vX.IntegerValueС:=form2.SpinEdit1.Value; //записываем значение поля через процедуру setProp, которая также вызывается неявно.