Siguiente nivel
Vamos a modificar el proyecto que usamos a modo de ejemplo en esta entrada para desacoplar las distintas capas y así llevarlo a un nivel superior en lo que a arquitectura e implementación se refiere.Interfaces
Primero vamos a declarar las interfaces, cuatro en total, una para la conexión con la base de datos, otra para el acceso a datos, otra para el proveedor de datos y otra para la lógica de datos.IDataConnection = interface(IInterface)
['{7C9D5EDD-EDF8-4587-8276-9504C0F1EF4E}']
procedure DoConfigureDataSet(aDataSet: TDataSet);
end;
IDataAccess = interface(IInterface)
['{CF319B42-93E2-48CA-AA04-1C8B8FFB78EA}']
function GetDataSet: TDataSet;
end;
IDataProvider = interface(IInterface)
['{56670237-2508-4561-8B34-009C3C447197}']
function GetProvider: TComponent;
end;
IDataLogic = interface(IInterface)
['{85F47C24-1AC9-4431-A9DA-43EE9C3A2D92}']
function GetDataSet(const aName: string): TDataSet;
procedure DoAbrir;
procedure DoGuardarCambios;
end;
Estas cuatro interfaces las vamos a declarar en su propia unidad llamada UInterfaces.
Clases
Lo siguiente es modificar las clases para que implementen su respectiva interface. Por ejemplo, el DataModule de lógica de datos debe implementar la interface IDataLogic.TdtmdlDLCountry = class(TDataModule, IDataLogic)
{ IDataLogic }
protected
function GetDataSet(const aName: string): TDataSet;
procedure DoAbrir;
procedure DoGuardarCambios;
{ IDataLogic }
Cuando una clase declara que implementa una interface tiene la obligación de implementar todos los métodos de la interface tal y como están declarados en la propia interface.
var
oDataConnectionClass: TComponentClass = nil;
oDataAccessClass: TComponentClass = nil;
oDataProviderClass: TComponentClass = nil;
oDataLogicClass: TComponentClass = nil;
Y en la unidad URegistrar asignamos las clases correspondientes.
procedure DoRegistrar;
begin
UClases.oDataConnectionClass := DMCon_IBX.TdtmdlCon_IBX;
UClases.oDataAccessClass := DMDACountry_IBX.TdtmdlDACountry_IBX;
UClases.oDataProviderClass := DMDSPCountry.TdtmdlDSPCountry;
UClases.oDataLogicClass := DMDLCountry.TdtmdlDLCountry;
end;
Registrar
Para poder desacoplar las clases que implementan las interfaces de las clases que las usan vamos a usar un mecanismo de registración muy simple. En la unidad UClases vamos a declarar variables para asignar las clases que implementan las interfaces.var
oDataConnectionClass: TComponentClass = nil;
oDataAccessClass: TComponentClass = nil;
oDataProviderClass: TComponentClass = nil;
oDataLogicClass: TComponentClass = nil;
Y en la unidad URegistrar asignamos las clases correspondientes.
procedure DoRegistrar;
begin
UClases.oDataConnectionClass := DMCon_IBX.TdtmdlCon_IBX;
UClases.oDataAccessClass := DMDACountry_IBX.TdtmdlDACountry_IBX;
UClases.oDataProviderClass := DMDSPCountry.TdtmdlDSPCountry;
UClases.oDataLogicClass := DMDLCountry.TdtmdlDLCountry;
end;
Las bondades de Delphi nos permiten hacer esta maravilla. Primero declaramos una variable de tipo TComponentClass y luego le asignamos una clase descendiente de TComponentClass.
{ new introduced members }
private
FDataProvider: IDataProvider;
protected
procedure DoDataProvider;
{ new introduced members }
procedure TdtmdlDLCountry.DoDataProvider;
begin
if not Assigned(FDataProvider) then
begin
if not Assigned(oDataProviderClass) then
raise Exception.Create('Clase proveedora de datos no asignada');
FDataProvider := oDataProviderClass.Create(nil) as IDataProvider;
end;
clntdtstCountry.SetProvider(FDataProvider.GetProvider);
end;
El DataModule de lógica de datos necesita una instancia del DataModule proveedor de datos. Pero no necesita saber nada sobre la clase en sí siempre y cuando dicha clase implemente la interface IDataProvider. Y aunque la variable oDataProviderClass sea de tipo TComponentClass, al crear una instancia se ejecutará el contructur de la clase asignada (en este caso, el constructor de TdtmdlDSPCountry)
Descargar proyecto Delphi 2010
Instancias
En las clases que las usan vamos a crear las instancias de dichas clases cuando sea necesario. Por ejemplo, en el DataModule de lógica de datos sería así:{ new introduced members }
private
FDataProvider: IDataProvider;
protected
procedure DoDataProvider;
{ new introduced members }
procedure TdtmdlDLCountry.DoDataProvider;
begin
if not Assigned(FDataProvider) then
begin
if not Assigned(oDataProviderClass) then
raise Exception.Create('Clase proveedora de datos no asignada');
FDataProvider := oDataProviderClass.Create(nil) as IDataProvider;
end;
clntdtstCountry.SetProvider(FDataProvider.GetProvider);
end;
El DataModule de lógica de datos necesita una instancia del DataModule proveedor de datos. Pero no necesita saber nada sobre la clase en sí siempre y cuando dicha clase implemente la interface IDataProvider. Y aunque la variable oDataProviderClass sea de tipo TComponentClass, al crear una instancia se ejecutará el contructur de la clase asignada (en este caso, el constructor de TdtmdlDSPCountry)
Descargar proyecto Delphi 2010