
Модели com объектов
Принцип разработки компонентов
Компонент должен быть полным. Одиночный метод (или функция), реализующий некоторый алгоритм, еще не является компонентом. Настоящий компонент должен содержать многочисленные свойства и методы, облегчающие его применение в самых разных приложениях. Многократное использование кода только тогда принесет ожидаемый выигрыш времени, когда этот код можно легко встраивать в разные приложения.
• Компонент должен быть целенаправленным. Это означает, что компонент не должен предназначаться для достижения сразу множества целей. Он должен выполнять вполне конкретную задачу, но делать это максимально эффективно. Если вам нужно решение спектра задач, гораздо полезнее (и проще!) создать серию взаимосвязанных компонентов.
ПРИМЕЧАНИЕ -
Нетрудно обнаружить некоторую противоречивость сформулированных тезисов. На практике следует найти соответствующий баланс между полнотой и целенаправленностью компонентов.
• Компонент должен делать нечто новое. Совершенно очевидное требование, которое, тем не менее, нередко игнорируется. Можно привести немало примеров компонентов IniFile или Registry, которые делают то же, что и стандартные классы TIniFile и TRegistry Delph
Свойства масивов и массив свойств\классов
Свойства-массивы имеют перечисленные ниже особенности.
• Свойства-массивы объявляются с указанием одного или нескольких индексных параметров. Тип индексов должен быть целым или строковым (в отличие от обычных массивов, свойства-массивы могут индексироваться строками).
• Доступ к значению свойства-массива может быть только косвенным (через методы SetXXXX/GetXXXX).
• Если в определении свойства-массива используется несколько индексов (многомерные свойства-массивы), методы доступа должны содержать параметры для каждого индекса в том же порядке, что и в объявлении свойства.
• Свойства-массивы нельзя объявлять в секции published. Доступ к значениям свойства-массива на этапе конструирования возможен только с помощью специализированного редактора свойств.
В качестве примера создадим класс TPlanets, который хранит названия всех планет Солнечной системы (листинг 7.1).
Листинг 7.1. Класс TPlanets, иллюстрирующий особенности свойств-массивов Unit Planets;
interface
uses
Classes, SysUtils; type
TPlanets = class(TComponent) private
// Методы доступа к свойству-массиву
function GetPlan^tName(const AIndex: Integer): String; function GetPlanetPosition(const AName: String): Integer; public
// Свойства-массивы нельзя публиковать!
// Следующее свойство возвращает название планеты по ее номеру property PlanetName[const AIndex: Integer]: String
read GetPlanetName; default; // Следующее свойство возвращает номер планеты по ее названию property PlanetPosition[const AName: String]: Integer
read GetPlanetPosition;
end; implementation const
PlanetNames: array [1..9] of String[8] = ('Меркурий1, 'Венера1, 'Земля', 'Марс', 'Юпитер', 'Сатурн', 'Нептун', 'Уран', 'Плутон');
продолжение &
Листинг 7.1 (продолжение)
function TPlanets.GetPlanetName(const AIndex: Integer): String; // Возвращает название планеты по ее номеру. Если номер выходит // за пределы диапазона, возбуждает исключение. begin
if Aindex in [1..9] then
Result := PlanetNames[AIndex] else
raise Exception.Create('Ошибочный номер')
end;
function TPlanets.GetPlanetPosition(const AName: String): Integer; // Возвращает номер планеты по ее имени. // Если имя неверно, возвращает 0.
к: Integer; begin к := 0; repeat
inc(к);
until (к = 10) or (CompareStr(AnsiUpperCase(AName),
AnsiUpperCase(PlanetNames[к]) = 0); if к < 10 then
Result := к else
Result := 0
end; end.
Обратите внимание: свойство-массив GetPlanetName определено с директивой default. Такое определение делает свойство-массив умалчиваемым и позволяет опускать название свойства при обращении к нужному значению. Два следующих оператора выводят одно и то же название:
ShowMessage(Planets.PlanetName[3]); ShowMessage(Planets[3]);
В классе можно сделать умалчиваемым только одно свойство-массив.
T.object
Класс TObject является предком всех других классов, используемых в DELPHI. Он включает в себя характеристики, свойственные всем используемым классам. Некоторые методы класса TObject могут использоваться без создания соответствующих объектов с учетом того, что реального объекта такого класса может и не быть. Эти методы позволяют получить общие характеристики класса – адрес таблицы, содержащей характеристики класса, имя класса, имя предка класса, характеристики методов и т. д. Примеры некоторых методов класса TObject: • ClassName – функция класса (типа ShortString) формирует строку, содержащую имя класса, данное ему при создании; • ClassParent – функция, определяющая класс непосредственного предка данного класса; • ClassType – функция возвращает класс конкретного объекта; • InstanceSize – функция (типа Longint) возвращает размер класса или объекта в байтах; • FieldAddress(Name) – функция типа Pointer возвращает адрес поля объекта с именем Name типа ShortString.
T.component
Класс TComponent является предком всех компонентов VCL. Он используется в качестве основы для создания невизуальных компонентов и реализует основные механизмы, которые обеспечивают функционирование любого компонента. В нем появляются первые свойства, которые отображаются в Инспекторе объектов. Это свойство
property Name: TComponentName;
Оно содержит имя экземпляра компонента, которое используется для идентификации компонента в приложении.
Примечание
Тип TComponentName представляет собой обычную строку:
type TComponentName = type string;
Свойство
property Tag: Longint;
является вспомогательным и не влияет на работу компонента. Оно отдано на откуп разработчику, который может присваивать ему значения по своему усмотрению. Например, это свойство можно использовать для дополнительной, более удобной идентификации компонентов.
Для компонентов существует своя иерархия, поэтому в классе введен механизм учета и управления компонентами, для которых данный компонент является владельцем. Свойства и методы, которые отвечают за управление, приведены в табл. 2.1.
Свойства и методы для управления списком компонентов
property Components [Index: Integer]: TComponent ; Содержит индексированный список указателей всех компонентов, для которых данный компонент является владельцем (owner)
property ComponentCount : Integer; Число подчиненных компонентов
property Owner: TComponent; Указывается, какой компонент является владельцем данного
property Componentlndex: Integer; Индекс данного компонента в списке владельца
procedure InsertComponent (AComponent : TComponent) ; Вставляет компонент AComponent в список
procedure RemoveComponent (AComponent : TComponent}; Удаляет компонент AComponent из списка
procedure FindComponent (AName: string): TComponent; Осуществляет поиск компонента по имени AName
procedure DestroyComponents; Предназначен для уничтожения всех компонентов, подчиненных данному
Очень важное свойство
type TComponentState = set of (csLoading, csReading, csWriting, csDestroying, csDesigning, csAncestor, csllpdating, csFixups, csFreeNotification, cslnline, csDesignlnstance); property ComponentState: TComponentState;
дает представление о текущем состоянии компонента. В табл. 2.2 описаны возможные состояния компонента. Состояние может измениться в результате получения компонентом некоторого сообщения, действий разработчика, выполнения акции и т. д. Это свойство активно используется средой разработки.
Возможные состояния компонента
csLoading Устанавливается при загрузке компонента из потока
csReading Устанавливается при чтении значений свойств из потока
csWriting Устанавливается при записи значений свойств в поток
csDestroying Устанавливается при уничтожении компонента
csDesigning Состояние разработки. Устанавливается при работе с формой во время разработки
csAncestor Устанавливается при переносе компонента на форму. Для перехода в это состояние должно быть уже установлено состояние csDesigning
csUpdatingУстанавливается при изменении значений свойств и отображения результата на форме-владельце. Для перехода в это состояние должно быть уже установлено состояние csAncestor
CsFixups Устанавливается, если компонент связан с компонентом другой формы, которая еще не загружена в среду разработки
csFreeNotification Если это состояние устанавливается, другие компоненты, связанные с данным, уведомляются о его уничтожении
cslnline Определяет компонент верхнего уровня в иерархии. Используется для обозначения корневого объекта в разворачивающихся свойствах
csDesignlnstance Определяет корневой компонент на этапе разработки
Для обеспечения работы механизма действий (см. гл. 8) предназначен метод
function ExecuteAction(Action: TBasicAction): Boolean; dynamic;
Он вызывается автоматически при необходимости выполнить акцию, предназначенную для данного компонента.
На уровне класса TComponent обеспечена поддержка СОМ-интерфейсов IUnknown и IDispatch.
Через свойство
property ComObject: IUnknown;
вы можете обеспечить применение методов этих интерфейсов.
Таким образом, класс TComponent имеет все для использования в качестве предка, для создания собственных невизуальных компонентов.