Что такое интерфейс? Грубо говоря, это класс без реализации. Т.е. когда вы пишете какой-то класс, у вас есть часть, записанная в секции interface модуля (например, тот же TForm1), и часть, записанная в секции implementation (код TForm1.Button1Click и т.п.). Первая часть - это интерфейс класса, вторая часть - реализация. Т.е. класс состоит из двух частей. Так вот, интерфейс (interface) - это только первая часть. Интерфейс - это своего рода контракт между двумя сторонами. Он говорит, как должны выглядеть вещи, чтобы быть понятными обоим сторонам, но ничего не говорит про то, как это реализовывается или используется. Для того, чтобы интерфейс работал, к нему нужно присоединить реализацию и где-то использовать. Но для начала его надо описать.
Итак, приведём описание интерфейса:
type IInit = interface ['{2104B774-A54E-4FC2-95DC-145A9877FF30}'] // public procedure Init(const AInitParams: IInterface); safecall; procedure Done; safecall; end;Давайте разберём, что тут написано.
IInit = interface - это объявление интерфейса. Некий аналог TForm1 = class. Как и классы, интерфейсы тоже можно наследовать, но в нашем случае мы делаем очень простой интерфейс, который не нуждается во вспомогательном предке (в отличие от класса TForm1, который почти всю свою функциональность наследует от TForm). По аналогии с объектами, которые всегда наследуются от TObject (даже если это не указано явно), все интерфейсы наследуются от IInterface (синоним IUnknown).
Далее следует идентификатор интерфейса ("['{2104B774-A54E-4FC2-95DC-145A9877FF30}']"). Чтобы его получить я нажал Ctrl + Shift+ G в редакторе Delphi. Эта штука называется GUID и в данном случае предназначена для уникальной идентификации интерфейса. Дело в том, что интерфейсы не имеют имён. Чтобы как-то отличать один интерфейс от другого им можно присваивать GUID. Сейчас это не обязательно, но будет нужно в дальнейшем.
Далее идут методы интерфейса. Поскольку в интерфейсе нет реализации, то нет и разделов private, protected и т.п., а все объявления считаются размещёнными в секции public. Раз нет реализации, значит нет и полей данных. Есть только методы. Также можно объявлять свойства.
Нам нужно два метода - Init и Done. В Init мы передаём дополнительный параметр. Это будут параметры инициализации приложения. Как уже было сказано выше, IInterface - это самый базовый интерфейс, который только возможен. Поэтому аналогом метода Init у классов был бы метод Init(Sender: TObject).
Передачу дополнительного параметра нужно предусматривать всегда. Даже, если он вам не нужен. Дело в том, что он вам может понадобиться в будуших версиях протокола. Если он вам не нужен сейчас - вы можете просто передавать туда пустой интерфейс. Но если вы его не предусмотрите, а в дальнейшем он вам понадобиться, то вам придётся вводить новую функцию инициализации. А в каждом плагине придётся делать по две функции инициализации - для старого и нового протоколов. Так вот, чтобы такого не было, мы вводим параметр у Init. Через него мы можем передать любые данные, какие только захотим.
Почему параметра нет у Done? Он там и не нужен. Done вызывается перед выгрузкой библиотеки. Она обязана безусловно произвести очистку. Какие ещё могут быть параметры? Все данные для работы были заданы заранее - в Init и/или других функциях (которые мы введём потом).
Что касается модели вызова, то она должна быть stdcall - модель вызова по-умолчанию в WinAPI (для переносимости между языками). Почему же у методов стоит safecall? Об этом - в следующий раз.
Примечание: подробнее интерфейсы разбираются в Использование интерфейсов, Урок 4. Сервер, кокласс, интерфейс, Урок 5. Интерфейс IUnknown (последние две ссылки рассматривают интерфейсы с точки зрения COM).
Ссылки в примечании (использование интерфейсов, урок 4 и 5) все ведут на Википедию/GUID
ОтветитьУдалитьОопс... Спасибо, исправил. Не уверен, правда, какой материал я имел ввиду тогда по первой ссылке.
ОтветитьУдалитьссылка вначале статьи:
ОтветитьУдалитьнаш интерфейс IInit.
ведет на 2-ую часть, хотя альтхинт говорит про пятую