Понятие интерфейса
Интерфейс IUnknown
Класс TInterfacedObject
Использование оператора as
Использование ключевого слова implements
Графический интерфейс пользователя. Создание SDI-и MDI-приложений
Консольные приложения
В этой главе мы рассмотрим, что такое интерфейс и где он применяется. Кроме
того, вы узнаете, что такое графический интерфейс пользователя, и познакомитесь
с методами создания SDI- и MDI- приложений.
Понятие интерфейса
Ключевое слово Kylix interface позволяет вам создавать и использовать интерфейсы
в ваших приложениях. Интерфейсы расширяют модель наследования в CLX, позволяя
одному классу принадлежать нескольким интерфейсам, а также нескольким классам
- наследникам различных базовых классов - использовать один интерфейс. Интерфейсы
полезны в тех случаях, когда наборы операций, таких как потоки, используются
большим количеством объектов.
Таким образом, интерфейсы - это средства для обеспечения взаимодействия между
разными объектами.
Интерфейсы похожи на классы, которые содержат в себе только абстрактные методы
и четкие определения их функциональности. Определение метода интерфейса включает
в себя параметры и типы параметров, возвращаемый тип, а также ожидаемое поведение.
Методы интерфейса семантически или логически связаны с отражением цели интерфейса.
Существует соглашение об интерфейсах, гласящее, что каждый интерфейс должен
быть назван в соответствии с задачей, которую он предназначен выполнять. Например,
интерфейс IMalloc - предназначен для распределения, освобождения и управления
памятью. Аналогично, интерфейс IPersist может использоваться как базовый интерфейс
для потомков, каждый из которых определяет специфичные прототипы методов для
загрузки и сохранения состояния объектов в память, поток или в файл. В листинге
10.1 приведен простой пример объявления интерфейса.
Листинг 10.1. Объявление интерфейса
type
IEdit = interface
procedure Copy; stdcall;
Iprocedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
Примечание
Тем читателям, которые имеют слабое представление о создании компонентов и новых
классов, советуем прочитать пятую часть книги, посвященную созданию собственных
компонентов и модификации уже существующих.
Неьзя создать экземпляр интерфейса при помощи интерфейса. Для получения экземпляра
интерфейса вам нужно объявить его в классе, содержащим данный интерфейс. Таким
образом, нужно определить класс, который держит необходимый интерфейс в списке
своих родителей (листинг 10.2).
Листинг 10.2. Объявление класса, содержащего интерфейс
TEditor = class (TInterfacedObject, lEdit)
procedure Copy; stdcall;
procedure Cut; stdcall;
rocedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Как уже было отмечено выше, использование интерфейсов позволяет нескольким классам
общаться с помощью общего интерфейса. При этом не обязательно наличие одного
базового класса-предка. Следует помнить, что интерфейс - это тип с управляемым
временем жизни, т. е. он автоматически при инициализации принимает значение
nil, обладает счетчиком ссылок тематически уничтожается при выходе за пределы
своей области видимости.
Интерфейс IUnknown
По аналогии с наследованием классов, предком которых является базовый ее TObject,
все интерфейсы являются прямыми или косвенными наследниками интерфейса lunknown.
Этот базовый интерфейс описан в модуле следующим образом (листинг 10.3).
Листинг 10.3. Описание базового интерфейса lunkhown :
type
lUnknown = interface
['{ 00000000-0000-0000-С000-000000000046}']
function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
Синтаксис описания интерфейса похож на описание класса. Главное отличие заключается
в том, что интерфейс может быть связан с глобальным уникальным идентификатором
(Global Unique Identifier, GUID).
GUID - это 128-разрядное целое число, которое используется для уникальной идентификации
интерфейсов. Так как в 128-ми разрядах можно закодировать большое количество
чисел, GUID гарантирует глобальную уникальность идентификатора интерфейса. То
есть, практически невозможно, чтобы на двух компьютерах GUID совпал. Алгоритм
генерации GUID основан на аппаратной части компьютера (частота процессора, номер
сетевой карты и т. д.). В результате работы алгоритма, который может быть реализован
с помощью функции API coCreatecuiDO, получается запись типа TGUID. Эту запись
можно определить в виде строки следующего формата:
'{хххххххх-хххх-хххх-хххх-хххххххххххх}'
Для создания нового GUID в среде Kylix достаточно нажать комбинацию клавиш <Ctii>+<Shift>+<G>
при работе редактора кода.
Итак, интерфейс lunknown поддерживает три метода, которые наследуются всеми
интерфейсами:
Queryinterfасе () - используется для создания запроса, поддерживается ли данный
интерфейс, и если поддерживается, то метод возвращает указатель на него. Предположим,
что имеется некоторый объект object, который поддерживает несколько интерфейсов:
interfacel, interface2 и др. Для получения указателя на интерфейс interface2
объекта object
Вам Нужно вызвать метод Interface2.QueryInterface() .
_AddRef о - используется, когда получен указатель на данный интерфейс, и вы
хотите использовать этот указатель. Метод _AddRef о обязательно должен заканчиваться
вызовом метода _Reiease {) .
_Release о - используется для завершения работы с интерфейсом.
Класс TlnterfacedObject
В CLX Kylix определен класс TlnterfacedObject, который служит базовым эм для
объектов интерфейса. Данный класс определен в модуле Kylix System (листинг 10.4).
Листинг 10.4. Определение класса TlnterfacedObject
type
TinterfacedObject = class(TObject, Ilnterface)
protected
FRefCount: Integer;
function Querylnterface(const IID: TGUID; out Obj): HResult;
stadcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
class function Newlnstance: TObject; override;
property RefCount: Integer read FRefCount;
end;
Как вы видите, данный класс в качестве родителей имеет класс TObject и интерфейс
IInterface. Класс TlnterfacedObject позволяет достаточно легко задавать классы,
поддерживающие интерфейсы. Например,
type
TMyObjInterfaced = class(TlnterfacedObject, IPaint)
...
end;
На вышеприведенном примере мы определяем новый класс TMyObjInterfaced, который
является прямым потомком класса terfacedobject и поддерживает некий интерфейс
IPaint.
Использование оператора as
Объекты, поддерживающие интерфейсы, могут использовать оператор as динамического
присоединения интерфейса. Например,
procedurePaintObjects(P: TlnterfacedObject)
var
X: IPaint;
begin
X := Р as IPaint;
{операторы}
end;
В данном примере переменная Р имеет тип Tinterfacedobject. Данная переменная
может быть назначена переменной х, как ссылка на интерфейс IPaint. Для данного
назначения компилятор генерирует код для вызова метода Queryinterface, относящегося
к интерфейсу lUnknown переменной р. Такое назначение возможно, даже если Р не
поддерживает данный интерфейс. То есть компилятор не выдаст ошибку при таком
назначении.
Во время выполнения вышеприведенного примера либо успешно происходит присваивание
Х:= Р as IPaint;
либо генерируется исключительная ситуация.
При использовании оператора as вы должны выполнять следующие требования:
при объявлении интерфейса явно объявляйте в качестве предка интерфейс lunknown,
т. к. только в этом случае вы сможете воспользоваться оператором as;
если вы используете оператор as для интерфейса, данный интерфейс должен иметь
свой IID. Напомним, что для создания нового IID достаточно, находясь в редакторе
кода, нажать комбинацию клавиш <Ctrl>+ +<Shift>+<G>.
Использование ключевого слова implements
Многие классы CLX Kylix имеют в качестве некоторых своих свойств объекты. Кроме
того, вы можете применять в качестве свойств класса интерфейсы. В том случае,
когда свойство имеет тип интерфейса, вы можете использовать ключевое слово implements
для определения методов, которые данный интерфейс передает объекту. По умолчанию
ключевое слово implements передает все методы интерфейса. Тем не менее, вы можете
самостоятельно определить список тех методов интерфейса, которые передаются
объекту.
В листинге 10.5 представлен пример использования ключевого слова implements
при создании объекта адаптера цвета, предназначенного для преобразования восьмибитного
значения цвета RGB. 10. Интерфейсы
Листинг 10.5. Использование ключевого слова implements
unit cadapt;
ty ре
IRGB8bit = interface
[' {1d76360a-f4f5-11d1-87d4-00c04fb17199}']
function Red: Byte;
function Green: Byte;
function Blue: Byte;
end;
IColorRef = interface ;
['{1d76360b-f4f5-11d1-87d4-00c04fb17199}']
function Color: Integer;
end;
TRGB8ColorRefAdapter = class(TInterfacedObject, IRGB8bit, IColorRef)
private
FRGB8bit: IRGB8bit;
FPalRelative: Boolean;
public
constructor Create(rgb: IRGB8bit);
property RGBSIntf: IRGBSbit read FRGB8bit implements IRGBSbit;
property PalRelative: Boolean read FPalRelative write FPalRelative;
function Color: Integer;
end;
implementation
constructor TRGB8ColorRefAdapter. Create (rgb: IRGB8bit);
begin
FRGB8bit := rgb;
end;
function TRGB8ColorRefAdapter. Color: Integer;
begin
if FPalRelative then
Result := PaletteRGB (RGB8Intf .Red, RGB8Intf .Green, RGB8Intf .Blue)
else
Result := RGB (RGB8Intf. Red, RGB8Intf .Green, RGB8Intf .Blue) ;
end;
end.
Графический интерфейс пользователя. Создание
SDI- и MDI-приложений
Kylix позволяет создавать приложения двух моделей пользовательского интерфейса:
одиночный интерфейс документа (Single document interface, SDI);
многооконный интерфейс документа (Multiple document interface, MDI).
В MDI-приложении в главном родительском окне может располагаться более чем одно
дочернее окно или документ. Такая форма наиболее часто используется в приложениях
электронных таблиц или текстовых процессорах. В SDI-пршюжении, напротив, может
содержаться только один документ или может быть активным всего одно окно. Для
того чтобы приложение имело вид SDI,
необходимо установить свойство FormStyle для формы приложения в fsNormal.
Примечание
По умолчанию свойство FormStyle у формы нового приложения устанавливается в
fsNormal.
Для создания нового SDI-приложения выполните следующие шаги:
1. Выберите в главном меню Kylix пункт File/New. Появится диалоговое окно New
Items (рис. 10.1).
Рис. 10.1. Диалоговое окно New Items
2. Щелкните на пиктограмме Application.
3. Нажмите кнопку ОК.
Для создания нового MDI-приложения выполните следующие шаги:
1. Выберите в главном меню Kylix пункт File/New для вызова диалогового окна
New Items.
2. Выберите в диалоговом окне вкладку Projects и щелкните на пиктограмме MDI
Application (рис. 10.2).
3. Нажмите кнопку ОК.
Рис. 10.2. Вкладка Projects
В MDI-приложении главная форма содержит несколько дочерних форм, рсоторые могут
размещаться внутри главной, но не могут выходить за ее пределы. Для определения,
какая форма будет главной, а какая дочерней, установите свойство Formstyle каждой
из форм в следующие значения:
fsMDiForm - для главной формы;
fsMDichild - для дочерних форм.
При создании MDI-приложения Kylix делает большую часть работы за программиста.
Если вы выполнили вышеперечисленные шаги для создания MDI-приложения, то можете
видеть, что Kylix уже создал главную форму (рис. 10.3).
Рис. 10.3. Главная форма MDI-приложения
Более того, данная форма имеет необходимые компоненты для работы со многими
дочерними окнами, а также главное меню для работы с файлами и окнами. Кроме
того, меню содержит пункт Edit для работы с текстом. Таким образом, Kylix уже
сделал для нас простейший многооконный текстовый редактор. Естественно, вы можете
самостоятельно расширить его функциональность либо удалить ненужные компоненты.
Рис. 10.4. Три расположенных рядом дочерних окна внутри главной формы
Запустите эту форму и попробуйте создать новые дочерние окна с помощью пункта
меню File/New или нажатия на соответствующую пиктограмму панели инструментов.
Вы можете создать несколько файлов и располагать их каскадом, рядом и другими
способами, с помощью нажатия на соответствующие пиктограммы панели инструментов
(рис. 10.4).
Для поддержания всей этой функциональности Kylix создал специальный код, который
размещен в модуле clxmain (листинг 10.6).
Листинг 10.6. Код, автоматически создаваемый для MDI-приложения:
unit clxmain;
linterface
uses SysUtils, Classes, QForms, QlmgList, QStdActns, QActnList, QDialogs,
QMenus, QTypes, QComCtrls, QControls, QExtCtrls;
type
TMainForm = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
FileNewItem: TMenuItem;
FileOpenltem: TMenuItem;
FileCloseltem: TMenuItem; Window1: TMenuItem;
Help1: TMenuItem;
N1: TMenuItem;
FileExitltem: TMenuItem;
WindowCascadeltem: TMenuItem;
WindowTileltem: TMenuItem;
HelpAboutltem: TMenuItem;
OpenDialog: TOpenDialog; FileSaveltem: TMenuItem;
FileSaveAsItem: TMenuItem;
Edit1: TMenuItem;
Cutltem: TMenuItem;
Copyltem: TMenuItem;
Pasteltem: TMenuItem;
WindowMinimizeltem: TMenuItem;
StatusBar: TStatusBar;
ActionList1: TActionList;
EditCut1: TEditCut;
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
FileNew1: TAction;
FileSave1: TAction;
FileExit1: TAction;
FileOpen1: TAction;
FileSaveAs1: TAction;
WindowCascade1: TWindowCascade;
WindowMinimizeAll1: TWindowMinimizeAll;
HelpAbout1: TAction;
FileClose1: TWindowClose;
WindowTileItem2: TMenuItem;
ToolBar2: TToolBar;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
ToolButton4: TToolButton;
ToolButton5: TToolButton;
ToolButton6: TToolButton;
ToolButton9: TToolButton;
ToolButton7: TToolButton;
ToolButton8: TToolButton;
ToolButton10: TToolButton;
ToolButton11: TToolButton;
WindowClose1: TWindowClose;
WindowTile1: TWindowTile;
ToolButton12: TToolButton;
ImageList1: TImageList;
procedure FileNewlExecute(Sender: TObject).;
procedure FileOpenlExecute(Sender: TObject);
procedure HelpAboutlExecute(Sender: TObject);
procedure FileExitlExecute(Sender: TObject);
private
{ Private declarations }
procedure CreateMDIChild(const Name: string);
public
{Public declarations }
end;
MainForm: TMainForm;
implementation
{$R.xfm}
uses clxchildwin, clxabout;
procedure TMainForm. CreateMDIChild (const Name: string);
var
Child: TMDIChild;
begin
{ создание нового дочернего MDI-окна }
Child := TMDIChild. Create (Application) ;
Child. Caption := Name;
if FileExists (Name) then Child. Memo1. Lines. LoadFromFile (Name) ,
end;
procedure TMainForm. FileNew1Execute (Sender: TObject) ;
begin
CreateMDIChild ('NONAME' + IntToStr (MDIChildCount + 1) );
end;
procedure TMainForm.FileOpen1Execute(Sender: TObject) ;
begin
if OpenDialog.Execute then
CreateMDIChild(OpenDialog.FileName);
end;
procedure TMainForm.HelpAbout1Execute(Sender: TObject);
begin
AboutBox.ShowModa1;
end;
procedure TMainForm.FileExit1Execute(Sender: TObject);
begin
Close;
end;
end.
Консольные приложения
Консольные приложения - это 32-разрядные приложения, которые могут работать
без загруженного графического интерфейса Linux.
Для создания нового консольного приложения выберите пункт главного меню Kylix
File/New и в появившемся диалоговом окне выберите пиктограмму Console Application
(рис. 10.5).
Рис. 10.5. Пиктограмма Console Application
Kylix создаст файл проекта для консольного приложения и покажет окно редактора
кода с текстом, приведенным в листинге 10.7.
Листинг 10.7. Заготовка для консольного приложения
program Project1;
{$APPTYPE CONSOLE} // Директива компилятора, означающая, что приложение
// будет консольным
begin
end.
Формы в консольных приложениях отсутствуют.
Консольные приложения пишутся на языке Object Pascal. Создание таких приложений
практически не отличается от создания программ на языке Pascal под DOS.
1. Электромагнитная волна (в религиозной терминологии релятивизма - "свет") имеет строго постоянную скорость 300 тыс.км/с, абсурдно не отсчитываемую ни от чего. Реально ЭМ-волны имеют разную скорость в веществе (например, ~200 тыс км/с в стекле и ~3 млн. км/с в поверхностных слоях металлов, разную скорость в эфире (см. статью "Температура эфира и красные смещения"), разную скорость для разных частот (см. статью "О скорости ЭМ-волн")
2. В релятивизме "свет" есть мифическое явление само по себе, а не физическая волна, являющаяся волнением определенной физической среды. Релятивистский "свет" - это волнение ничего в ничем. У него нет среды-носителя колебаний.
3. В релятивизме возможны манипуляции со временем (замедление), поэтому там нарушаются основополагающие для любой науки принцип причинности и принцип строгой логичности. В релятивизме при скорости света время останавливается (поэтому в нем абсурдно говорить о частоте фотона). В релятивизме возможны такие насилия над разумом, как утверждение о взаимном превышении возраста близнецов, движущихся с субсветовой скоростью, и прочие издевательства над логикой, присущие любой религии.
4. В гравитационном релятивизме (ОТО) вопреки наблюдаемым фактам утверждается об угловом отклонении ЭМ-волн в пустом пространстве под действием гравитации. Однако астрономам известно, что свет от затменных двойных звезд не подвержен такому отклонению, а те "подтверждающие теорию Эйнштейна факты", которые якобы наблюдались А. Эддингтоном в 1919 году в отношении Солнца, являются фальсификацией. Подробнее читайте в FAQ по эфирной физике.