Interface inheritance without generics - delphi

I am trying to implement an interface to convert records in a dataset to Delphi records in a pre-generics version of Delphi. I don't like the interface at the moment, as it will always need calls to Supports which I'd like to avoid if possible and was wondering if there's a better way of doing it that I'm missing.
So far I have an navigation interface and data retrieval interface defined:
IBaseRecordCollection = interface
procedure First;
procedure Next;
function BOF: boolean;
... // other dataset nav stuff
end;
IRecARecordCollection = interface
function GetRec: TRecA;
end;
IRecBRecordCollection = interface
function GetRec: TRecB;
end;
Basically I have a concrete base class that contains a private dataset and implements IBaseRecordCollection and concrete class for each RecordCollection interface which derives from an abstract class implementing the IBaseRecordCollection (handled by an implements property) with the implementation of the record retrieval routine:
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRecARecordCollection);
public
function GetRec: TRecA;
end;
Now, to use this I'm forced to have a builder that returns a IRecARecordCollection and then mess around with Supports, which I'm not keen on as it will always be used in this fashion.
i.e.
procedure GetMyRecASet;
var
lRecARecordCollection: IRecARecordCollection;
lRecordCollection: IBaseRecordCollection;
begin
lRecARecordCollection := BuildRecACollection;
if not supports(lRecARecordCollection, IBaseRecordCollection, lRecordCollection) then
raise exception.create();
while not lRecordCollection.EOF do
begin
lRecARecordCollection.GetRec.DoStuff;
lRecordCollection.Next;
end;
end;
Although this works, I'm not keen on the supports call and mixing my lRecordCollections and my lRecARecordCollections like this. I had originally hoped to be able to do something like:
IBaseRecordCollection = interface
// DBNav stuff
end;
IRecARecordCollection = interface (IBaseRecordCollection)
function GetRec: TRecA;
end;
TRec1RecordCollection = class(TInterfacedObject, IRecARecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
function GetRec: TRecA;
end;
but unfortunately Delphi wasn't smart enough to realise that the implementation of IRecARecordCollection was split over the base IBaseRecordCollection in the Collection property implements call and the TRec1RecordCollection object.
Are there any other suggestions for neater ways to acheive this?
-- edit to give a (longer) reply to #David's answer than possible in a comment
The suggested solution of:
IBaseRecordCollection = interface ['{C910BD0A-26F4-4682-BC82-605C4C8F9173}']
function GetRecNo: integer;
function GetRecCount: integer;
function GetFieldList: TFieldList;
function EOF: boolean;
function BOF: boolean;
...
end;
IRec1RecordCollection = interface (IBaseRecordCollection) ['{E12F9F6D-6D57-4C7D-AB87-8DD50D35DCA2}']
function GetRec: TRec1;
property Rec: TRec1 read GetRec;
end;
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRec1RecordCollection, IBaseRecordCollection)
private
function GetRec: TRec1;
public
property Rec: TRec1 read GetRec;
end;
isn't compiling. It's complaining that TRec1RecordCollection cannot find methods related to IBaseRecordCollection. I also tried moving the Collection property from Abstract to Rec1RecordCollection and redeclaring the property in TRec1RecordCollection all with the same result
Looking a bit deeper it appears that direct inheritance of a class implementing IBaseRecordCollection would work but Delphi can't handle doing it indirectly via a property using implements.

Your code is almost there. The implements directive in your code fails to compile because you only declared that your class implements the derived interface. As it stands, your class does not implement the interface that the implements directive refers to, namely IBaseRecordCollection. You might think that would be inferred from the inheritance but it is not.
To solve your problem you simply need to declare that TRec1RecordCollection implements both interfaces:
type
TRec1RecordCollection = class(TInterfacedObject, IBaseRecordCollection,
IRecARecordCollection)
....
end;
Make just the one small change and your code will compile.
Update
Your edit to the question changes this somewhat. The code in my answer does indeed compile, given the code in your original question. However, add any method into IBaseRecordCollection and the compile will not accept it.
The compiler should accept this code and the fact that it does not is because of a compiler bug. Modern versions of Delphi will accept the code in your update to the question.
Unless you upgrade your compiler you will not be able to make your intended design work.

Related

How use multi interface class in Spring4D

I'm just learning Spring4D and i have one question.
If class implement only one interface its all clear:
IWeapon = interface
['{E679EDA6-5D43-44AD-8F96-3B5BD43A147B}']
procedure Attack;
end;
TSword = class(TInterfacedObject, IWeapon)
public
procedure Attack;
end;
GlobalContainer.RegisterType<TSword>.Implements<IWeapon>('sword');
sword := ServiceLocator.GetService<IWeapon>('sword');
and im really happy now, I have sword and i dont need to Free it.
but if class implements two or more interfaces:
IWeapon = interface
['{E679EDA6-5D43-44AD-8F96-3B5BD43A147B}']
procedure Attack;
end;
IShield = interface
['{B2B2F443-85FE-489C-BAF4-538BB5B377B3}']
function Block: Integer;
end;
TSpikedShield = class(TInterfacedObject, IWeapon, IShield)
public
function Block: Integer;
procedure Attack;
end;
GlobalContainer.RegisterType<TSpikedShield>.Implements<IWeapon>.Implements<IShield>;
I can ask ServiceLocator for an instance of TSpikedShield but i need choose one IWeapon or IShield. But I want use it in two ways(or i shouldn't want?) like:
spikedShield.Attack;
spikedShield.Block;
So if I good undestand, I have to create instance of TSpikedShiled directly(i mean without interface).
function MakeSpikedShield: TSpickedShield;
begin
result := TSpickedShield.Create;
end;
There is any way to use this class but with automagical Free?
(there won't be problem if interfaces could implement multi interfeces but its not allowed in delphi)
Edited:
maybe somethink like that?
ISpikedSield = interface
function AsWeapon: IWeapon;
function AsShield: IShield;
end;
TSpikedShield = class(TInterfacedObject, ISpikedShield)
There won't be problem if interfaces could implement multi interfaces but it's not allowed in Delphi
That is the exact cause of the problem.
I would just make an ISpikedShield interface that has the methods of IWeapon and IShield and making sure that every class that implements ISpikedShield also explicitly implements IWeapon and IShield (this is what the compiler basically does for you in C# for example where an interface can inherit from multiple other interfaces).
You then cannot assign an ISpikedShield to an IWeapon and IShield but using as operator will work because the class behind implements them.
However I am not sure if there is not a misconception in your architecture because if you think further there won't be a class that has an ISpikedShield as dependency but rather an IWeapon and/or IShield. Some game code would then check if your IShield supports ICanAttack to do an additional hit apart from those you can do with your IWeapon.

Must I define both the forward declaration and the interface of a class in the same section?

I have a class that I want to keep private, because I only use it in the implementation section.
However it is used by a class that is declared public in the interface section.
Is there a way to do something like this:
unit x;
interface
type
TPrivate = class; //forward declaration
TPublic = class(TSomething)
private
FPrivate: TPrivate;
procedure DoStuffWithFPrivate;
public
//...
end;
implementation
type
TPrivate = class(TObject)
procedure Test;
end;
Obviously the above code gives an error:
[dcc32 Error] UnitX.pas(27): E2086 Type 'TPrivate' is not yet completely defined
I don't want to resort to cheap tricks like:
FPrivate = TObject
....
procedure TPublic.DoStuffWithFPrivate;
begin
TPrivate(FPrivate).Test;
Is there a way to do what I want without having to spill TPrivate's internal details in the interface?
I know it's possible to declare TPrivate as a strict private sub type of TPublic, however I don't like the pollution of the interface section this gives.
Is there a way to keep TPrivate out the of interface section (as much as possible) whilst maintaining type safety?
As long as TPrivate is not used anywhere in the interface of TPublic besides the field declaration (f.i. as a methods parameter type) you can use a local class helper to achieve this.
Note: FPrivate is not a good name for that field!
interface
type
THiddenActual = class
end;
TPublic = class
private
FActual: THiddenActual;
procedure DoStuffWithFPrivate;
public
end;
implementation
type
TActual = class(THiddenActual)
public
procedure Foo;
end;
type
TPublicHelper = class helper for TPublic
private
function GetActual: TActual;
procedure SetActual(const Value: TActual);
public
property Actual: TActual read GetActual write SetActual;
end;
procedure TActual.Foo;
begin
end;
function TPublicHelper.GetActual: TActual;
begin
Result := FActual as TActual;
end;
procedure TPublicHelper.SetActual(const Value: TActual);
begin
FActual := Value;
end;
procedure TPublic.DoStuffWithFPrivate;
begin
Actual.Foo;
end;
OK, it is merely a little variance of that cheap trick, but what are the alternatives? You have to take what is available, don't you?
I'm not sure what exactly your definition of "pollution" is as regards the interface section, but if it's just a matter of keeping it from drowning your TPublic class definition with noise then one option might be simply inheritance :
TBasePublic = class(TSomething)
private
type
TPrivate = class
// ...
// ... keep TPrivate definition separate
end;
end;
TPublic = class(TBasePublic)
private
FPrivate : TPrivate;
end;
This would even allow you to define TPrivate in a completely different unit if segregation is your goal.

Add an interface to a class afterwards

Is it possible to add and implement an interface to an already existing class (which is a descendant of TInterfaced or TInterfacedPersistent) to accomplish separating Model and View into 2 units?
A small explanation why I need something like this:
I am developing a tree-structure, open-type model, which has following structure (VERY simplified and incomplete, just to illustrate the outline of the problem):
Database_Kernel.pas
TVMDNode = class(TInterfacedPersistent);
public
class function ClassGUID: TGUID; virtual; abstract; // constant. used for RTTI
property RawData: TBytes {...};
constructor Create(ARawData: TBytes);
function GetParent: TVMDNode;
function GetChildNodes: TList<TVMDNode>;
end;
Vendor_Specific_Stuff.pas
TImageNode = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property Image: TImage {...};
end;
TUTF8Node = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property StringContent: WideString {...};
end;
TContactNode = class(TVMDNode)
public
class function ClassGUID: TGUID; override; // constant. used for RTTI
// Will be interpreted out of the raw binary data of the inherited class
property PreName: WideString {...};
property FamilyName: WideString {...};
property Address: WideString {...};
property Birthday: TDate {...};
end;
Using a GUID-based RTTI (which uses ClassGUID), the function GetChildNodes is able to find the matching class and initialize it with the raw data. (Each dataset contains ClassGUID and RawData beside other data like created/updated timestamps)
It is important to notice that my API (Database_Kernel.pas) is strictly separated from the vendor's node classes (Vendor_Specific_Stuff.pas).
A vendor-specific program's GUI wants to visualize the nodes, e.g. giving them an user-friendly name, an icon etc.
Following idea works:
IGraphicNode = interface(IInterface)
function Visible: boolean;
function Icon: TIcon;
function UserFriendlyName: string;
end;
The vendor's specific descendants of TVMDNode in Vendor_Specific_Stuff.pas will implement the IGraphicNode interface.
But the vendor also needs to change Database_Kernel.pas to implement IGraphicNode to the base node class TVMDNode (which is used for "unknown" nodes, where RTTI was unable to find the matching class of the dataset, so at least the binary raw data can be read using TVMDNode.RawData).
So he will change my class as follows:
TVMDNode = class(TInterfacedPersistent, IGraphicNode);
public
property RawData: TBytes {...};
class function ClassGUID: TGUID; virtual; abstract; // constant. used for RTTI
constructor Create(ARawData: TBytes);
function GetParent: TVMDNode;
function GetChildNodes: TList<TVMDNode>;
// --- IGraphicNode
function Visible: boolean; virtual; // default behavior for unknown nodes: False
function Icon: TIcon; virtual; // default behavior for unknown nodes: "?" icon
function UserfriendlyName: string; virtual; // default behavior for unknown nodes: "Unknown"
end;
The problem is that IGraphicNode is vendor/program-specific and should not be in the API's Database_Kernel.pas, since GUI and Model/API should be strictly divided.
My wish would be that the interace IGraphicNode could be added and implemented to the existing TVMDNode class (which is already a descendant of TInterfacedPersistent to allow interfaces) in a separate unit. As far as I know, Delphi does not support something like this.
Beside the fact that it is not nice to mix Model and View in one single unit/class, there will be following real-world problem: If the vendor has to change my Database_Kernel.pas API to extend TVMDNode with the IGraphicNode interface, he needs to re-do all his changes, as soon as I release a new version of my API Database_Kernel.pas.
What should I do? I thought very long about possible solutions possible with Delphi's OOP. A workaround may be nesting TVMDNode's into a container class, which has a secondary RTTI, so after I have found the TVMDNode class, I could search for a TVMDNodeGUIContainer class. But this sounds very strangle and like a dirty hack.
PS: This API is an OpenSource/GPL project. I am trying to stay compatible with old generations of Delphi (e.g. 6), since I want to maximize the number of possible users. However, if a solution of the problem above is only possible with the new generation of Delphi languages, I might consider dropping Delphi 6 support for this API.
Yes it is possible.
We implemented something similar to gain control of global/singletons for testing purposes. We changed our singletons to be accessible as interfaces on the application (not TApplication, our own equivalent). Then we added the ability to dynamically add/remove interfaces at run-time. Now our test cases are able to plug in suitable mocks as and when needed.
I'll describe the general approach, hopefully you'll be able to apply it to the specifics of your situation.
Add a field to hold a list of dynamically added interface. An TInterfaceList works nicely.
Add methods to add/remove the dynamic interfaces.
Override function QueryInterface(const IID: TGUID; out Obj): HResult; virtual;. Your implementation will first check the interface list, and if not found will defer to the base implementation.
Edit: Sample Code
To answer your question:
I understand that the class now can tell others that it supports interface X now, so the interface was ADDED during runtime. But I also need to IMPLEMENT the interface's methods from outside (another unit). How is this done?
When you add the interface, you're adding an instance of the object that implements the interface. This is very much like the normal property ... implements <interface> technique to delegate implementation of an interface to another object. The key difference being this is dynamic. As such it will have the same kinds of limitations: E.g. no access to the "host" unless explicitly given a reference.
The following DUnit test case demonstrates a simplified version of the technique in action.
unit tdDynamicInterfaces;
interface
uses
SysUtils,
Classes,
TestFramework;
type
TTestDynamicInterfaces = class(TTestCase)
published
procedure TestUseDynamicInterface;
end;
type
ISayHello = interface
['{6F6DDDE3-F9A5-407E-B5A4-CDF91791A05B}']
function SayHello: string;
end;
implementation
{ ImpGlobal }
type
TDynamicInterfaces = class(TInterfacedObject, IInterface)
{ We must explicitly state that we are implementing IInterface so that
our implementation of QueryInterface is used. }
private
FDynamicInterfaces: TInterfaceList;
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
public
constructor Create;
destructor Destroy; override;
procedure AddInterface(AImplementedInterface: IInterface);
end;
type
TImplementor = class (TInterfacedObject, ISayHello)
{ NOTE: This could easily have been implemented in a separate unit. }
protected
{ISayHello}
function SayHello: string;
end;
{ TDynamicInterfaces }
procedure TDynamicInterfaces.AddInterface(AImplementedInterface: IInterface);
begin
{ The simplest, but least flexible approach (see also QueryInterface).
Other options entail tagging specific GUIDs to be associated with given
implementation instance. Then it becomes feasible to check for duplicates
and also dynamically remove specific interfaces. }
FDynamicInterfaces.Add(AImplementedInterface);
end;
constructor TDynamicInterfaces.Create;
begin
inherited Create;
FDynamicInterfaces := TInterfaceList.Create;
end;
destructor TDynamicInterfaces.Destroy;
begin
FDynamicInterfaces.Free;
inherited Destroy;
end;
function TDynamicInterfaces.QueryInterface(const IID: TGUID; out Obj): HResult;
var
LIntf: IInterface;
begin
{ This implementation basically means the first implementor added will be
returned in cases where multiple implementors support the same interface. }
for LIntf in FDynamicInterfaces do
begin
if Supports(LIntf, IID, Obj) then
begin
Result := S_OK;
Exit;
end;
end;
Result := inherited QueryInterface(IID, Obj);
end;
{ TImplementor }
function TImplementor.SayHello: string;
begin
Result := 'Hello. My name is, ' + ClassName;
end;
{ TTestDynamicInterfaces }
procedure TTestDynamicInterfaces.TestUseDynamicInterface;
var
LDynamicInterfaceObject: TDynamicInterfaces;
LInterfaceRef: IUnknown;
LFriend: ISayHello;
LActualResult: string;
begin
LActualResult := '';
{ Use ObjRef for convenience to not declare interface with "AddInterface" }
LDynamicInterfaceObject := TDynamicInterfaces.Create;
{ But lifetime is still managed by the InterfaceRef. }
LInterfaceRef := LDynamicInterfaceObject;
{ Comment out the next line to see what happens when support for
interface is not dynamically added. }
LDynamicInterfaceObject.AddInterface(TImplementor.Create);
if Supports(LInterfaceRef, ISayHello, LFriend) then
begin
LFriend := LInterfaceRef as ISayHello;
LActualResult := LFriend.SayHello;
end;
CheckEqualsString('Hello. My name is, TImplementor', LActualResult);
end;
end.
You can preserve the ability to persist data and implement it through inheritance and still create the correct instances for the ClassGUIDs stored in the tables if you'd apply the factory design pattern.
For each node class there would be one class factory (or just a function pointer) responsible for creation of the correct Delphi class. Class factories may register themselves in the unit initialization section (once per application startup) at the kernel singleton object.
The kernel singleton would then map GUID to correct factory that would in turn call the correct class instance constructor (as shown at http://delphipatterns.blog.com/2011/03/23/abstract-factory)
Packages may be split into separate DLLs and classes implemented in separate units, still inheriting from one base TVMNode class.
The features you now use RTTI for can be supported in descendant classes or in the factory classes easily through some virtual methods.
You might also consider using simpler Data Transfer Objects for saving/loading the TVMNodes and perhaps take some inspiration from an already well perceived Object Relational Mapper or a Object Persistence framework as the problem you are trying to solve seem to me like exactly the problems they are handling (already)
I don't know about good Delphi open source frameworks of this class. But from other languages you can look at Java Hibernate, Microsoft .NET Entity Framework or minimalistic Google Protocol Buffers serializer

Delphi interface generic function - Is there a work around?

I have the following code
IProxy<T> = interface
['{1E3A98C5-78BA-4D65-A4BA-B6992B8B4783}']
function Setup : ISetup<T>;
function Proxy : T;
function CastAs<I : IInterface> : IInterface;
end;
Is there a way to work around the compiler error that is received when compiling?
"[DCC Error] Delphi.Mocks.pas(123): E2535 Interface methods must not have parameterized methods"
Basically I would like to have this interface be passed around and be able to cast off it, by passing in the type to cast to and having that type returned. I can achieve this with a class, however would prefer passing around an interface.
Additional Info:
Say I have the following class
TInterfaceProxy<T> = class(TBaseProxy<T>)
private type
TProxyVirtualInterface = class(TVirtualInterface)
private
FProxy : TInterfaceProxy<T>;
protected
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; override; stdcall;
constructor Create(AProxy : TInterfaceProxy<T>; AInterface: Pointer; InvokeEvent: TVirtualInterfaceInvokeEvent);
end;
private
FVirtualInterfaces : TDictionary<TGUID, TProxyVirtualInterface>;
protected
function InternalQueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HRESULT; override;
function Proxy : T;override;
function CastAs<I: IInterface> : I;
public
constructor Create;override;
destructor Destroy;override;
end;
CastAs works well here as the newly requested cast can be created a new virtual interface. Now if I want to pass this class around its fine. However if I require it as an interface i.e. TInterfaceProxy<T> = class(TBaseProxy<T>, IProxy<T>) it doesn't work understand that. Don't agree with it, but understand.
Therefore how do I get around this restriction so that I can call a CastAs function, pass in a type (any interface type to start with), and be able to create a virtual interface from it?
Interfaces do not support generic parameterized methods, as the compiler says.
There is no workaround because it's a fundamental limitation. Parameterized methods in classes are implemented by adding one method per instantiation to the class. That works for classes since they are concrete, but is not viable for interfaces. That's because interfaces are a table of functions and the size of that table cannot vary depending on which generic method instantiations happen to be present elsewhere in the code. For similar reasons, generic methods cannot be virtual or dynamic.
The code in your question is a little mis-leading also. You wrote:
function CastAs<I : IInterface> : IInterface;
but I'm sure you meant:
function CastAs<I : IInterface> : I;
In any case, it's not possible. One option is to use a class instead. I agree that this is a bind.
If you want to do it in an interface, the best you can do is:
function CastAs(const IID: TGUID): IInterface;
But you'd have to call it like this:
MyIntf := ProxyIntf.CastAs(IMyIntf) as IMyIntf;
which feels somewhat foul.
Choose your poison!
As the error message states, a method in an interface cannot have Generic parameters. The compiler simply does not support it, and this is documented as such:
http://docwiki.embarcadero.com/RADStudio/XE4/en/Overview_of_Generics
Parameterized method in interface
A parameterized method (method declared with type parameters) cannot be declared in an interface.
In other words, your CastAs method is illegal because it is declared in an interface type. On the other hand, you don't need such a method in the first place. You can use SysUtils.Supports() instead for casting one interface to another.
Something like the following compiles for me with Delphi 11.0 (yet to see if it crashes):
TStoryItemList = TList<IStoryItem>;
TObjectListEx<T: class> = class
class function GetAllOfInterface<AInterface: IInterface>(const list: TList<T>): TList<AInterface>;
end;
//...
class function TObjectListEx<T>.GetAllOfInterface<AInterface>(const list: TList<T>): TList<AInterface>;
var
itemAsAInterface: AInterface;
begin
var guid := TRttiInterfaceType(TRttiContext.Create.GetType(TypeInfo(AInterface))).GUID;
var listOfAInterface := TList<AInterface>.Create;
for var item in list do
if Supports(item, guid, itemAsAInterface) then
listOfAInterface.Add(itemAsAInterface);
result := listOfAInterface;
end;
usage
function TStoryItem.GetStoryItems: TStoryItemList;
begin
result := TObjectListEx<TControl>.GetAllOfInterface<IStoryItem>(Controls);
end;

Testing a class not declared in the interface section

I'm a newbie with Dependency Injection containers, and I am trying to get my head around using them in conjunction with Mocking.
Lets say I have a controller and a list (the model):
IBlahList = interface
property Items[AIndex: integer]: IBlah read GetItem;
end;
IController = interface
property List: IBlahList read GetList;
end;
The implementation of IController would look something like (note, it's in the implementaion section:
implementation
TController = class (TInterfacedObject, IController)
private
FList: IBlahList;
function GetList: IBlahList;
public
constructor Create(const AList: IBlahList);
end;
And then, of course, I would register this class (as well as one for IBlahList) with the GlobalContainer:
GlobalContainer.RegisterType<TController>.Implements<IController>;
I place the TController in the implementation section, as suggested by various sources (well, Nick Hodges anyway!), so that we cannot reference the TController class directly.
Now, just say I want to test my implementation of ICollection in a unit test:
procedure TestSomething
var
LMockList: TMock<IBlahList>;
LController: IController;
begin
LMockList := TMock<IBlahList>.Create;
// Oops, I can't do this, I can't access TController
LController := TController.Create(LMockList);
end;
So, my question is, should I move the TController class into my interface section so I can test it, or is there some other way to pass the mock IBlahList to the controller that I have yet to find?
If you have the concrete class in the implementation section, then you could expose a factory function (i.e. have it in the interface section) that creates an IController with the required parameters.
It makes absolutely no sense to have an implementation that can not be instantiated, IMO.
interface
...
function CreateController(AList: IBlahList): IController;
implementation
function CreateController(AList: IBlahList): IController;
begin
Result := TController.Create(AList);
end;
Well you probably should be using the mock framework in your test projects as well, but in these cases I usually "cheat" and move the implementation to where I need it using a DUNIT conditional variable:
// In the real app, we want the implementation and uses clauses here.
{$IFNDEF DUNIT}
implementation
uses
classes;
{$ENDIF}
type
TClassUnderTest = class(TObject)
// ...
end;
// In test projects it is more convenient to have the implemenation and
// uses clauses down here.
{$IFDEF DUNIT}
implementation
uses
classes;
{$ENDIF}
Then make sure that any test projects define the DUNIT conditional var, and move any units needed by the TClassUnderTest declaration to the interface section. The latter you can do permanently or under control of the DUNIT conditional as well.
I can just say: don't listen to Nick in that case.
Putting a class inside the implementation part of a unit just has disadvantages and you are facing one of them.
The whole point of using dependency injection is to decouple pieces of your code.
Now you removed the static dependency of TController and some class that implements IBlahList but you pulled in another (and much worse imo) dependency: the dependency on the DI container.
Don't put the class inside the implementation part of a unit just to prevent someone from directly creating it in your production code. Also don't put in the dependency on the DI container into that unit.
A much better approach is to have 3 units: interface, class, registration.
Edit:
I suggest reading this article and pay attention to the underlined parts: http://www.loosecouplings.com/2011/01/dependency-injection-using-di-container.html
Edit2 - added some pseudo code to show what I mean.
The unit test code could exactly be as in the question.
unit Interfaces;
interface
type
IBlahList = interface
property Items[AIndex: integer]: IBlah read GetItem;
end;
IController = interface
property List: IBlahList read GetList;
end;
implementation
end.
-
unit Controller;
interface
uses
Classes,
Interfaces;
type
TController = class (TInterfacedObject, IController)
private
FList: IBlahList;
function GetList: IBlahList;
public
constructor Create(const AList: IBlahList);
end;
implementation
...
end.
-
unit Registration;
interface
implementation
uses
Interfaces,
Controller,
Spring.Container;
initialization
GlobalContainer.RegisterType<TController>.Implements<IController>;
end.

Resources