I'm making a framework (for internal use only) that has common code among 3 o 4 delphi database CRUD applications..
A common object of mi framework is a TContext,
TContext = class (IContext)
function DB: IDatabase;
function CurrentSettings: ISettings;
..
end;
that is passed to the initialization method of lots of other objects.. example (this will be application code):
TCustomer.Initialize(Context: IContext)
TProjectList.Initialize(Context: IContext)
..
Every application has some specific context functions (that only will be called from application code):
IApp1Context = interface (IContext)
procedure DoSomethingSpecificToApp1;
procedure DoOtherThing;
..
end;
So when I create a Context, Im creating a IApp1Context, and sending it to the initialization methods.. from the framework code everything is fine, the problem is that from the application code I'm constantly casting from IContext to IApp1Context to access the specific App1 functions.. so all my application code looks like (and its a lot of code like this):
(FContext as IApp1Context).DoSomethingSpecificToApp1
(FContext as IApp1Context).DoOtherThing;
..
The thing is clearly usable, but doesnt reads well in my opinion. Maybe I'm exaggerating; is there is a clever design technique for this situation that I'm not aware of?
Use a temporary variable. Assign it once at the start of the method, and then use it where you need it.
var
AppContext: IApp1Context;
begin
AppContext := FContext as IApp1Context;
AppContext.DoSomethingSpecificToApp1;
AppContext.DoOtherThing;
end;
Or, since it looks like your IContext
object is a field of an object, make your IApp1Context
variable be a field of the same object. It could even replace the IContext
field since IApp1Context
already exposes everything the IContext
does.
procedure TCustomer.Initialize(const Context: IContext);
begin
FContext := Context;
FAppContext := FContext as IApp1Context;
// ...
end;
What do you think of this possible solution using generics?
pro: no casting necesary down: the generic invades almost every interface and class of the framework, making it more complicated..
// framework //
type
IContext = interface
function DB;
..
end;
TContext = class (TInterfaedObject, IContext)
function DB;
..
end;
IBusinessObj<T: IContext> = interface
procedure Initialize(AContext: T);
end;
TBusinessObj<T: IContext> = class (TInterfacedObject, IBusinessObj<T>)
protected
FContext: T;
public
procedure Initialize(Context: T); virtual;
end;
procedure TBusnessObj<T>.Initialize(Context: T);
begin
FContext := Context;
FContext.DB.Connect;
end;
// application //
type
IApp1Context = interface (IContext)
procedure DoSomethingElse;
..
end;
TApp1Context = class(TContext, IContext, IApp1Context)
function DB;
procedure DoSomethingElse;
end;
TCustomer = class(TBusinessObj<IApp1Context>)
public
procedure Initialize(AContext: IApp1Context); override;
end;
procedure Start;
var
C: IBusinessObj<IApp1Context>;
begin
C := TCustomer.Create;
C.Initializate(TApp1Context.Create);
..
end;
procedure TCustomer.Initialize(AContext: IApp1Context);
begin
inherited;
FContext.DoSomethingElse // here I can use FContext as a IApp1Context..
end;
Comment please, Thanks!
You could also give your class a private function AppContext
defined like this:
function AppContext : IApp1Context;
begin
Result := FContext as IApp1Context;
end;
This avoids the additional variable declaration and keeps the cast local. From client code you can just write:
AppContext.DoSomethingSpecificToApp1;
AppContext.DoOtherThing
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With