For one particular issue in the architecture of an application I’m working on, interfaces seem to be a nice solution. Specifically, some ‘business objects’ depend on a bunch of settings that are pulled from the database in the actual app. Letting those business objects ask for an interface (through Inversion of Control), and letting a central TDatabaseSettings object implement those interfaces, allows for better isolation, and thus for much easier unit testing.
However, in Delphi, interfaces seem to come with an, in this case, unpleasant bonus: reference counting. This means that if I do something like this:
type IMySettings = interface function getMySetting: String; end; TDatabaseSettings = class(..., IMySettings) //... end; TMyBusinessObject = class(TInterfacedObject, IMySettings) property Settings: IMySettings read FSettings write FSettings; end; var DatabaseSettings: TDatabaseSettings; // global object (normally placed in a controller somewhere) //Now, in some function... O := TMyBusinessObject.Create; O.Settings := DatabaseSettings; // ... do something with O O.Free;
On the last line (O.Free), my global DatabaseSettings object is now also freed, since the last interface reference to it (which was contained in O) is lost!
One solution would be to store the ‘global’ DatabaseSettings object with an interface; another solution would be to override the reference counting mechanism for the TDatabaseSettings class, so I can continue to manage the DatabaseSettings as a normal object (which is much more consistent with the rest of the app).
So, in summary, my question is: how do I disable the interface reference counting mechanism for a particular class?
I’ve been able to find some info that suggests overriding the IInterface methods _AddRef and _Release for the class (TDatabaseSettings in the example); has anyone ever done that?
Or would you say I shouldn’t do this (confusing? just a bad idea?), and find a different solution to the architectural problem?
Thanks a lot!
Ok, you can bypass it, but the question is if you really want that. If you want to use interfaces, you better use them completely. So as you have experienced it, you get problems if you mix class and interface variables.
You now have a second reference to the interface and losing the first will not free the object.
It as also possible to keep both the class and the object:
Be sure to set the interface right after the object has been created.
If you really want to disable reference counting, you just have to create a new descendant of TObject that implements IInterface. I have tested the example below in D2009 and it works:
FreeRef just lowers the refcount just like _Release. You can use it where you normally use Free.