ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 10.01.2024
Просмотров: 285
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ЛР 1. Операторы и выражения Delphi
Управление жизненным циклом объекта
Ограничение видимости членов класса
Особенности объявления методов
Принадлежность к родительскому контейнеру
Размещение и размеры элемента управления
Видимость и активность элемента управления
ЛР5. Обработка клавиатурных событий и событий мышки
, то создание и уничтожение объекта будет осуществлено за счет конструктора и деструктора родительского класса. В нашем примере мы не стали создавать индивидуальные методы Create() и Destroy() для двигателя TEngine. Это означает, что управление существованием двигателя будет осуществляться за счет унаследованных им методов класса-предка (в нашем случае TObject).
Практический аспект применения конструктора и деструктора рассмотрен в листинге 3.7.
Листинг 3.7. Создание и уничтожение объекта в коде программы
var A : TAutomobile; //объектная переменная
begin
A:=TAutomobile.Create; //создаем объект
... //операции с объектом
A.Destroy; //разрушаем объект
end;
Разрабатывая даже столь простую имитацию транспортного средства, нам все равно хочется достичь схожего поведения класса TAutomobile и реального автомобиля. Именно из-за этого, наиболее интересной частью кода стал метод SetSpeed(), управляющий скоростью машины. Благодаря программной логике, заложенной в упомянутый метод (см. листинг 5), мы не позволим набрать скорость автомобилю, если у него не включен двигатель. Предлагаю поставить перед собой еще одну задачу — при выключении двигателя скорость автомобиля должна упасть до 0.
Если вы ненадолго вернетесь к листингу 3, в котором представлены методы класса TEngine, то вы сразу вспомните, что за остановку двигателя отвечает процедура SetEnabled() с аргументом false. На первый взгляд, может показаться, что мы находимся в одном шаге от решения поставленной задачи — надо лишь дополнить метод SetEnabled() строкой кода
if Value=false then fAutomobile.fSpeed:=0;
Однако на практике это невозможно. Причина проста — класс TEngine не имеет ни малейшего представления даже о существовании класса TAutomobile, не говоря уже о его полях и методах. Поэтому упоминание поля автомобиля fSpeed в методе двигателя приведет к одному
— Delphi скажет, что идентификатор не объявлен (undeclared identifier) и откажется компилировать проект.
Какой выход из создавшегося положения? Ответ таков: надо сделать так, чтобы двигатель знал, в какую машину он установлен. Для этого я введу в состав класса TEngine еще одно поле — fAutomobile:TAutomobile. Однако в нашем листинге класс TAutomobile объявлен после класса TEngine, поэтому двигатель по-прежнему не понимает, что такое TAutomobile. Для устранения этой проблемы воспользуемся еще одной хитростью Delphi — опережающим объявлениемкласса.
Суть идеи опережающего объявления заключается в том, что мы заявим о намерении описать класс TAutomobile практически в первых строках листинга, а реальное описание оставим на прежнем месте (после описания двигателя). Благодаря такой уловке класс TEngine перестанет отвергать поле fAutomobile.
Листинг 3.8 содержит усовершенствованное объявление классов TEngine и TAutomobile.
Листинг 3.8. Улучшенный способ объявления классов TEngine и TAutomobile
{начало объявлений, одно ключевое слово type для TEngine и TAutomobile}
type
TAutomobile=class; //опережающее объявление класса TAutomobile
{полное объявление класса TEngine}
TEngine=class fEnabled :boolean;
fAutomobile :TAutomobile; //автомобиль, в который установлен двигатель
//другие поля и методы класса
end;
{полное объявление класса TAutomobile} TAutomobile=class(TObject)
fSpeed:single; //скорость движения
//другие поля и методы класса
end;
Обязательно обратите внимание на небольшое изменение в порядке объявления классов. Раньше (см. листинги 3.2 и 3.3) описание каждого из классов начиналось с отдельного ключевого слова type, в новой версии кода для обеспечения видимости опережающего объявления класса TAutomobile
мы ограничились одним словом type.
Еще одна синтаксическая особенность опережающего объявления в том, что заявление о существовании класса заканчивается не ключевым словом end, как того требует синтаксис (листинг 1). Вместо этого точка с запятой ставится сразу после инструкции class.
Добившись того, что класс TEngine узнал о существовании класса TAutomobile, усовершенствуем конструктор автомобиля (листинг 3.9).
Листинг 3.9. Исправление конструктора класса TAutomobile
constructor TAutomobile.Create;
begin
fEngine:=TEngine.Create; //создаем двигатель fEngine.fAutomobile:=self; //двигатель получил ссылку на автомобиль fEngine.SetEnabled(False); //исходное состояние — двигатель выключен
end;
Теперь, в момент создания автомобиля, ссылка на экземпляр класса TAutomobile будет передана в соответствующее поле экземпляра класса TEngine. Благодаря ссылке двигатель получает возможность обращаться к полям и методам автомобиля.
Нам осталось сделать последний штрих — дополнить метод включения/отключения двигателя
SetEnabled() командой на остановку автомобиля (листинг 3.10).
Листинг 3.10. Остановка автомобиля в момент выключения двигателя
procedure TEngine.SetEnabled(Value: Boolean);
begin
fEnabled:=Value;
if Value=false then
if Assigned(fAutomobile) then {если есть связанный объект} fAutomobile.fSpeed:=0;
end;
При описании класса программист имеет право определять степень доступности (видимости) его полей, свойств и методов. Это один из способов защиты наиболее критичных элементов класса от несанкционированного вмешательства сторонних разработчиков. Область видимости поля (метода) класса зависит от того, в какой из четырех возможных секций оно объявлено: private, protected, public и published (листинг 3.11).
Листинг 3.11. Секции видимости членов класса
type
TAutomobile = class private
... { секция частных объявлений }
protected
... { секция защищенных объявлений }
public
... { секция общих объявлений }
published
... { секция опубликованных объявлений }
end;
Поля и методы, доступ к которым нежелателен, обычно размещаются в секциях private и protected. Наиболее защищена секция private. К размещенным в ней полям и методам возможно обращение только из того же программного модуля, в котором описан этот класс. Секция protected несколько приоткрывает завесу секретности — находящаяся в ней информация без каких-либо ограничений доступна для классов-потомков. Секция public предоставляет объявленные в ней поля и методы для общего пользования всем желающим. Секция published самая доброжелательная. Например, объявленными в ней данными умеет пользоваться Инспектор объектов. Это возможно благодаря тому, что для этой секции класса генерируется информация о времени выполнения (Run Time Type Information, RTTI). Поэтому в секции published обычно объявляют все свойства и обработчики событий объекта.
Как правило, объект не должен предоставлять прямой доступ к своим полям. Это требование инкапсуляции, благодаря которому поддерживается целостность объекта. Поэтому при проектировании класса одновременно с полями объявляют особых посредников, называемых
свойствами (properties).
В первую очередь в обязанность свойств входит проверка корректности передаваемых в поля данных, для этого они могут задействовать все мощь языка Delphi. В самом простейшем случае синтаксическая конструкция объявления свойства выглядит следующим образом:
property Имя_свойства: Тип_свойстваRead (способчтения)
Write (способзаписи);
За инструкциями Read и Write могут следовать названия процедур, соответственно отвечающих за чтение данных из поля и запись данных в поле.
Возьмем в качестве примера класс двигателя TEngine, он обладает парой полей fEnabled и fAutomobile, которые следует обязательно защитить. Листинг 3.12 демонстрирует один из возможных сценариев усовершенствования класса.
Листинг 3.12. Объявление свойств
type TEngine=class private
fEnabled :boolean; //двигатель включен - true, выключен - false fAutomobile:TAutomobile; //ссылка на автомобиль
public
procedure SetEnabled(Value:Boolean); //запуск или останов двигателя
function GetEngineState:boolean; //состояние двигателя
published
property Enabled:boolean Read fEnabled Write SetEnabled;
property Automobile:TAutomobile Read fAutomobile;
end;
Все поля класса "спрятаны" в секцию private, что существенно усложнит обращение к полям извне. Наоборот, методы и свойства, к которым мы намерены разрешить полный доступ, размещены в секции публичных объявлений. Теперь для включения/отключения двигателя надо воспользоваться
Практический аспект применения конструктора и деструктора рассмотрен в листинге 3.7.
Листинг 3.7. Создание и уничтожение объекта в коде программы
var A : TAutomobile; //объектная переменная
begin
A:=TAutomobile.Create; //создаем объект
... //операции с объектом
A.Destroy; //разрушаем объект
end;
Опережающее объявление класса
Разрабатывая даже столь простую имитацию транспортного средства, нам все равно хочется достичь схожего поведения класса TAutomobile и реального автомобиля. Именно из-за этого, наиболее интересной частью кода стал метод SetSpeed(), управляющий скоростью машины. Благодаря программной логике, заложенной в упомянутый метод (см. листинг 5), мы не позволим набрать скорость автомобилю, если у него не включен двигатель. Предлагаю поставить перед собой еще одну задачу — при выключении двигателя скорость автомобиля должна упасть до 0.
Если вы ненадолго вернетесь к листингу 3, в котором представлены методы класса TEngine, то вы сразу вспомните, что за остановку двигателя отвечает процедура SetEnabled() с аргументом false. На первый взгляд, может показаться, что мы находимся в одном шаге от решения поставленной задачи — надо лишь дополнить метод SetEnabled() строкой кода
if Value=false then fAutomobile.fSpeed:=0;
Однако на практике это невозможно. Причина проста — класс TEngine не имеет ни малейшего представления даже о существовании класса TAutomobile, не говоря уже о его полях и методах. Поэтому упоминание поля автомобиля fSpeed в методе двигателя приведет к одному
— Delphi скажет, что идентификатор не объявлен (undeclared identifier) и откажется компилировать проект.
Какой выход из создавшегося положения? Ответ таков: надо сделать так, чтобы двигатель знал, в какую машину он установлен. Для этого я введу в состав класса TEngine еще одно поле — fAutomobile:TAutomobile. Однако в нашем листинге класс TAutomobile объявлен после класса TEngine, поэтому двигатель по-прежнему не понимает, что такое TAutomobile. Для устранения этой проблемы воспользуемся еще одной хитростью Delphi — опережающим объявлениемкласса.
Суть идеи опережающего объявления заключается в том, что мы заявим о намерении описать класс TAutomobile практически в первых строках листинга, а реальное описание оставим на прежнем месте (после описания двигателя). Благодаря такой уловке класс TEngine перестанет отвергать поле fAutomobile.
Листинг 3.8 содержит усовершенствованное объявление классов TEngine и TAutomobile.
Листинг 3.8. Улучшенный способ объявления классов TEngine и TAutomobile
{начало объявлений, одно ключевое слово type для TEngine и TAutomobile}
type
TAutomobile=class; //опережающее объявление класса TAutomobile
{полное объявление класса TEngine}
TEngine=class fEnabled :boolean;
fAutomobile :TAutomobile; //автомобиль, в который установлен двигатель
//другие поля и методы класса
end;
{полное объявление класса TAutomobile} TAutomobile=class(TObject)
fSpeed:single; //скорость движения
//другие поля и методы класса
end;
Обязательно обратите внимание на небольшое изменение в порядке объявления классов. Раньше (см. листинги 3.2 и 3.3) описание каждого из классов начиналось с отдельного ключевого слова type, в новой версии кода для обеспечения видимости опережающего объявления класса TAutomobile
мы ограничились одним словом type.
Еще одна синтаксическая особенность опережающего объявления в том, что заявление о существовании класса заканчивается не ключевым словом end, как того требует синтаксис (листинг 1). Вместо этого точка с запятой ставится сразу после инструкции class.
Добившись того, что класс TEngine узнал о существовании класса TAutomobile, усовершенствуем конструктор автомобиля (листинг 3.9).
Листинг 3.9. Исправление конструктора класса TAutomobile
constructor TAutomobile.Create;
begin
fEngine:=TEngine.Create; //создаем двигатель fEngine.fAutomobile:=self; //двигатель получил ссылку на автомобиль fEngine.SetEnabled(False); //исходное состояние — двигатель выключен
end;
Теперь, в момент создания автомобиля, ссылка на экземпляр класса TAutomobile будет передана в соответствующее поле экземпляра класса TEngine. Благодаря ссылке двигатель получает возможность обращаться к полям и методам автомобиля.
Нам осталось сделать последний штрих — дополнить метод включения/отключения двигателя
SetEnabled() командой на остановку автомобиля (листинг 3.10).
Листинг 3.10. Остановка автомобиля в момент выключения двигателя
procedure TEngine.SetEnabled(Value: Boolean);
begin
fEnabled:=Value;
if Value=false then
if Assigned(fAutomobile) then {если есть связанный объект} fAutomobile.fSpeed:=0;
end;
Ограничение видимости членов класса
При описании класса программист имеет право определять степень доступности (видимости) его полей, свойств и методов. Это один из способов защиты наиболее критичных элементов класса от несанкционированного вмешательства сторонних разработчиков. Область видимости поля (метода) класса зависит от того, в какой из четырех возможных секций оно объявлено: private, protected, public и published (листинг 3.11).
Листинг 3.11. Секции видимости членов класса
type
TAutomobile = class private
... { секция частных объявлений }
protected
... { секция защищенных объявлений }
public
... { секция общих объявлений }
published
... { секция опубликованных объявлений }
end;
Поля и методы, доступ к которым нежелателен, обычно размещаются в секциях private и protected. Наиболее защищена секция private. К размещенным в ней полям и методам возможно обращение только из того же программного модуля, в котором описан этот класс. Секция protected несколько приоткрывает завесу секретности — находящаяся в ней информация без каких-либо ограничений доступна для классов-потомков. Секция public предоставляет объявленные в ней поля и методы для общего пользования всем желающим. Секция published самая доброжелательная. Например, объявленными в ней данными умеет пользоваться Инспектор объектов. Это возможно благодаря тому, что для этой секции класса генерируется информация о времени выполнения (Run Time Type Information, RTTI). Поэтому в секции published обычно объявляют все свойства и обработчики событий объекта.
Свойства объекта
Как правило, объект не должен предоставлять прямой доступ к своим полям. Это требование инкапсуляции, благодаря которому поддерживается целостность объекта. Поэтому при проектировании класса одновременно с полями объявляют особых посредников, называемых
свойствами (properties).
В первую очередь в обязанность свойств входит проверка корректности передаваемых в поля данных, для этого они могут задействовать все мощь языка Delphi. В самом простейшем случае синтаксическая конструкция объявления свойства выглядит следующим образом:
property Имя_свойства: Тип_свойстваRead (способчтения)
Write (способзаписи);
За инструкциями Read и Write могут следовать названия процедур, соответственно отвечающих за чтение данных из поля и запись данных в поле.
Возьмем в качестве примера класс двигателя TEngine, он обладает парой полей fEnabled и fAutomobile, которые следует обязательно защитить. Листинг 3.12 демонстрирует один из возможных сценариев усовершенствования класса.
Листинг 3.12. Объявление свойств
type TEngine=class private
fEnabled :boolean; //двигатель включен - true, выключен - false fAutomobile:TAutomobile; //ссылка на автомобиль
public
procedure SetEnabled(Value:Boolean); //запуск или останов двигателя
function GetEngineState:boolean; //состояние двигателя
published
property Enabled:boolean Read fEnabled Write SetEnabled;
property Automobile:TAutomobile Read fAutomobile;
end;
Все поля класса "спрятаны" в секцию private, что существенно усложнит обращение к полям извне. Наоборот, методы и свойства, к которым мы намерены разрешить полный доступ, размещены в секции публичных объявлений. Теперь для включения/отключения двигателя надо воспользоваться