Ver Mensaje Individual
  #5  
Antiguo 25-08-2005
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Reputación: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Hola, voy a hablar un poco en el aire porque no he revisado esto con detenimiento.

A mi me parece que hay una confusión de conceptos.

Cita:
Empezado por adlfv
Como el programa principal utiliza _BD, tengo que agregar al proyecto UBD, pero es sólo para "engañar" al compilador y para que logre compilar, pues el objeto como tal reside en el paquete...
Realmente no estás engañando al compilador, simplemente estás enlazando el paquete a tu aplicación estáticamente. Esto quiere decir que tu ejecutable sabe desde un principio qué bpl buscar y por ende es innecesario cargarlo dinámicamente con LoadPackage.

Al hacer esto (carga estática de paquetes), sigues teniendo la ventaja de la modularidad que proporcionan los paquetes: puedes hacer cambios en el paquete sin tener que recompilar ni redistribuir toda la aplicación porque el paquete como tal no está en el ejecutable, únicamente el enlace a él.

Así que, antes de proseguir te conviene preguntarte si no te es suficiente esto. Posiblemente lo sea y ya no has de darle más vueltas al asunto.

El uso de paquetes dinámicos- aquellos cargados dinámicamente con LoadPackage -tiene otra finalidad, una ventaja extra sobre los paquetes estáticos.

Veamos un ejemplo.

Tienes una aplicación que exporta datos a una base. Para ello tienes una rutina en tu aplicación principal que recibe un DataSet:


Código Delphi [-]
procedure Exportar(DataSet: TDataSet);
begin
  DataSet.Open;
  while condicion do
  begin
    DataSet.Append;

    DataSet.FieldByName(...) := valor;
    DataSet.FieldByName(...) := valor;

    ...

    DataSet.Post;
  end;
end;

Supón ahora que la aplicación debe poder exportar los datos bien sea a MySql, Firebird o cualquier otro motor que el cliente desee en un futuro.

Para ello debes proporcionar a la rutina de exportación el DataSet adecuado al motor (TZTable, TIBTable, etc...).

Claro que lo más fácil sería:


Código Delphi [-]
if ExportarAMySql then
  Exportar(ZTable)
else if ExportarAFirebird then
  Exportar(IBTable);

pero esto condena tu aplicación a usar sólo esos dos motores. Para cualquier otro tendrás que agregar la condición correspondiente, recompilar y redistribuir toda la aplicación.

Claro que puedes obtener el DataSet de un paquete; pero si el enlace es estático el cliente tendría que cambiar uno por otro cada vez y reiniciar la aplicación.

Aquí es donde la carga dinámica adquiere sentido. El cliente proporciona, desde la aplicación, el nombre del paquete, lo cargas con LoadPackage y obtienes el DataSet que proporcione ese paquete.


Si una necesidad similar es la que realmente requieres entonces sí tendrás que estudiar como obtener el objeto BD del cuál hablas.

Regresando al principio; incluir la unidad en tu aplicación no sirve porque fuerzas un enlace estático.

La solución

Cita:
Empezado por jmariano
Para hacer esto lo único que tienes que hacer es especificar el unit "UBD" (donde se encuentra declarado el objeto _BD) en la cláusula "uses" del .dpr pero sin añadir "UBD" al proyecto (porque si lo añades, entonces, no usará el del paquete).
hasta donde yo entiendo no es correcta. Si quitas el paquete del proyecto pero dejas la referencia en la cláusula uses lo que sucederá es que tu aplicación ya no sólo se enlazará estáticamente al paquete, sino que de hecho lo integrará al ejecutable. Aun cuando logres apuntar al objeto que está en el paquete cargado dinámicamente, la realidad es que tendrás dos copias del objeto, con lo cual pierde sentido el cargarlo dinámicamente.

Para usar paquetes dinámicos- evitando el enlace estático, tu aplicación no puede contener ninguna referencia al paquete, ni en la lista de run time packages ni en las cláusulas uses.

Si no hay ninguna referencia entonces ¿cómo puedes usar los objetos de los paquetes?

Una posible solución es mediante el uso de clases abstractas. Defines una clase base que declare los métodos que requiera tu aplicación pero que no los implemente:


Código Delphi [-]
// DB_Base.pas

type
  TBDBase = class
    procedure HazAlgo; virtual; abstract;
  end;

Esta clase la colocas o bien directamente en tu aplicación o bien en un paquete enlazado estáticamente. Con esto tu aplicación compila sin problemas y al ejecutable sólo le estás agregando, por así decirlo, la definición de los métodos.

La implementación real de la clase (una clase descendiente) la colocarías en un paquete que, éste sí, cargarás dinámicamente:


Código Delphi [-]
// DB_Zeos.pas

interface

function CrearObjetoDB: TDBBase;

exports CrearObjetoDB;

implementation

uses DB_Base;

type
  TDBZeos = class(TDBBase)
    procedure HazAlgo; override;
  end;

procedure TDBZeos.HazAlgo;
begin
  {
    Aquí toda la implementación para Zeos
  }
end;

function CrearObjetoDB: TDBBase;
begin
  Result := TDBZeos.Create;
end;

Como ves, el paquete también enlaza estáticamente la clase base. Pero esto no es una carga ya ésta clase sólo define pero no implementa.

Nota que la clase descendiente TDBZeos ni siquiera tiene que estar en la sección interface.

En tu aplicación principal cargas el paquete con LoadPackage tal como lo has hecho y obtienes la dirección de la función CrearObjetoDB que exporta el paquete:

Código Delphi [-]
var
  PackModule: HModule;
  CrearObjetoBd: function: TBDBase;
  BD: TBDBase;

begin
  PackModule := LoadPackage('bdzeos.bpl');
  @CrearObjetoBD := GetProcAddress(PackModule, 'CrearObjetoBD');
  BD := CrearObjetoBD;
  BD.HazAlgo;

Como la aplicación sí usa la unidad DB_Base que define la clase base, el compilador no protesta ni en la declaración de la variable BD ni en la llamada al método HazAlgo.

Pero el polimorfismo hace que el objeto real sea de tipo TDBZeos.

En cualquier momento puedes programar otro paquete para otro motor de datos y tu aplicación estará lista para usarlo tan sólo especificando el nombre del paquete.

Nota además, que ni siquiera se requiere el uso de RegisterClass ni GetClass con lo cual no estás restringido a usar descendientes de TPersistent.

Espero no estar equivocado en los conceptos y que esto te aclare las cosas.

// Saludos
Responder Con Cita