Embarcadero Delphi
Interfaces
Buscar..
Observaciones
Las interfaces se utilizan para describir la información necesaria y el resultado esperado de los métodos y clases, sin proporcionar información de la implementación explícita.
Las clases pueden implementar interfaces, y las interfaces pueden heredarse unas de otras. Si una clase está implementando una interfaz, esto significa que todas las funciones y procedimientos expuestos por la interfaz existen en la clase.
Un aspecto especial de las interfaces en delphi es que las instancias de las interfaces tienen una administración de por vida basada en el conteo de referencias. El tiempo de vida de las instancias de clase debe gestionarse manualmente.
Teniendo en cuenta todos estos aspectos, las interfaces se pueden utilizar para lograr diferentes objetivos:
- Proporcionar múltiples implementaciones diferentes para las operaciones (por ejemplo, guardar en un archivo, base de datos o enviar como correo electrónico, todo como interfaz "Guardar datos")
- Reduzca las dependencias, mejorando el desacoplamiento y haciendo así que el código sea más fácil de mantener y verificable
- Trabaje con instancias en varias unidades sin tener problemas con la administración de por vida (aunque incluso aquí existen trampas, ¡tenga cuidado!)
Definir e implementar una interfaz.
Una interfaz se declara como una clase, pero sin modificadores de acceso ( public
, private
, ...). Además, no se permiten definiciones, por lo que no se pueden usar variables y constantes.
Las interfaces siempre deben tener un identificador único , que se puede generar presionando Ctrl + Shift + G.
IRepository = interface
['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Para implementar una interfaz, el nombre de la interfaz debe agregarse detrás de la clase base. Además, la clase debe ser descendiente de TInterfacedObject
(esto es importante para la administración de por vida ).
TDatabaseRepository = class(TInterfacedObject, IRepository)
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Cuando una clase implementa una interfaz, debe incluir todos los métodos y funciones declarados en la interfaz, de lo contrario no compilará.
Una cosa que vale la pena mencionar es que los modificadores de acceso no tienen ninguna influencia, si la persona que llama trabaja con la interfaz. Por ejemplo, todas las funciones de la interfaz se pueden implementar como miembros strict private
, pero aún se pueden llamar desde otra clase si se utiliza una instancia de la interfaz.
Implementando multiples interfaces
Las clases pueden implementar más de una interfaz, en lugar de heredar de más de una clase ( herencia múltiple ) que no es posible para las clases de Delphi. Para lograr esto, el nombre de todas las interfaces se debe agregar separados por comas detrás de la clase base.
Por supuesto, la clase implementadora también debe definir las funciones declaradas por cada una de las interfaces.
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;
Herencia para interfaces
Las interfaces pueden heredarse unas de otras, exactamente igual que las clases. Por lo tanto, una clase implementadora tiene que implementar funciones de la interfaz y todas las interfaces base. De esta manera, sin embargo, el compilador no sabe que la clase implementadora también implementa la interfaz base, solo conoce las interfaces que se enumeran explícitamente. Es por eso que el uso as ISuperInterface
en TImplementer
no funcionaría. Eso también resulta en la práctica común, implementar explícitamente todas las interfaces base, también (en este caso 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;
Propiedades en interfaces
Dado que la declaración de variables en interfaces no es posible, no se puede utilizar la forma "rápida" de definir propiedades ( property Value: TObject read FValue write FValue;
). En su lugar, el Getter y el setter (cada uno solo si es necesario) también deben declararse en la interfaz.
IInterface = interface(IInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
Una cosa que vale la pena mencionar es que la clase implementadora no tiene que declarar la propiedad. El compilador aceptaría este código:
TImplementer = class(TInterfacedObject, IInterface)
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
end;
Una advertencia, sin embargo, es que de esta manera solo se puede acceder a la propiedad a través de una instancia de la interfaz, y no a través de la propia clase. Además, agregar la propiedad a la clase aumenta la legibilidad.