Embarcadero Delphi
Интерфейсы
Поиск…
замечания
Интерфейсы используются для описания необходимой информации и ожидаемого вывода методов и классов без предоставления информации о явной реализации.
Классы могут реализовывать интерфейсы, а интерфейсы могут наследовать друг от друга. Если класс реализует интерфейс, это означает, что все функции и процедуры, открытые интерфейсом, существуют в классе.
Особым аспектом интерфейсов в delphi является то, что экземпляры интерфейсов имеют управление жизненным циклом, основанное на подсчете ссылок. Время жизни экземпляров класса должно управляться вручную.
Учитывая все эти аспекты, интерфейсы могут использоваться для достижения разных целей:
- Предоставление нескольких различных реализаций для операций (например, сохранение в файле, базе данных или отправка в виде E-Mail, все как интерфейс «SaveData»)
- Сокращение зависимостей, улучшение развязки и, таким образом, улучшение кода обслуживания и проверки кода
- Работайте с экземплярами в нескольких единицах, не беспокоясь о пожизненном управлении (хотя даже здесь существуют ошибки, остерегайтесь!)
Определение и реализация интерфейса
Интерфейс объявляется как класс, но без модификаторов доступа ( public
, private
, ...). Кроме того, никакие определения не допускаются, поэтому переменные и константы не могут использоваться.
Интерфейсы всегда должны иметь уникальный идентификатор , который можно сгенерировать, нажав Ctrl + Shift + G.
IRepository = interface
['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Чтобы реализовать интерфейс, имя интерфейса должно быть добавлено за базовым классом. Кроме того, класс должен быть потомком TInterfacedObject
(это важно для управления жизненным TInterfacedObject
).
TDatabaseRepository = class(TInterfacedObject, IRepository)
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Когда класс реализует интерфейс, он должен включать все методы и функции, объявленные в интерфейсе, иначе он не будет компилироваться.
Следует отметить, что модификаторы доступа не имеют никакого влияния, если вызывающий абонент работает с интерфейсом. Например, все функции интерфейса могут быть реализованы как strict private
члены, но все равно могут быть вызваны из другого класса, если используется экземпляр интерфейса.
Реализация нескольких интерфейсов
Классы могут реализовывать более одного интерфейса, в отличие от наследования более чем одного класса ( множественное наследование ), которое невозможно для классов Delphi. Чтобы достичь этого, имя всех интерфейсов должно быть добавлено запятыми позади базового класса.
Разумеется, класс реализации должен также определять функции, объявленные каждым из интерфейсов.
IInterface1 = interface
['{A2437023-7606-4551-8D5A-1709212254AF}']
procedure Method1();
function Method2(): Boolean;
end;
IInterface2 = interface
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
TImplementer = class(TInterfacedObject, IInterface1, IInterface2)
// IInterface1
procedure Method1();
function Method2(): Boolean;
// IInterface2
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
property Value: TObject read GetValue write SetValue;
end;
Наследование для интерфейсов
Интерфейсы могут наследовать друг от друга, точно так же, как и классы. Таким образом, реализующий класс должен реализовывать функции интерфейса и всех базовых интерфейсов. Таким образом, однако, компилятор не знает, что класс имплантации также реализует базовый интерфейс, он знает только о интерфейсах, которые явно указаны. Вот почему использование as ISuperInterface
в TImplementer
не будет работать. Это также приводит к обычной практике, чтобы явно реализовать все базовые интерфейсы (в этом случае TImplementer = class(TInterfacedObject, IDescendantInterface, ISuperInterface)
).
ISuperInterface = interface
['{A2437023-7606-4551-8D5A-1709212254AF}']
procedure Method1();
function Method2(): Boolean;
end;
IDescendantInterface = interface(ISuperInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
TImplementer = class(TInterfacedObject, IDescendantInterface)
// ISuperInterface
procedure Method1();
function Method2(): Boolean;
// IDescendantInterface
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
property Value: TObject read GetValue write SetValue;
end;
Свойства в интерфейсах
Поскольку объявление переменных в интерфейсах невозможно, «быстрый» способ определения свойств ( property Value: TObject read FValue write FValue;
) не может быть использован. Вместо этого, Getter и setter (каждый только в случае необходимости) также должны быть объявлены в интерфейсе.
IInterface = interface(IInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
Следует отметить, что класс реализации не должен объявлять свойство. Компилятор примет этот код:
TImplementer = class(TInterfacedObject, IInterface)
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
end;
Однако одно предостережение заключается в том, что таким образом свойство можно получить только через экземпляр интерфейса, а не через сам класс. Кроме того, добавление свойства в класс повышает читаемость.