I have read that interfaces are a good thing to decopule the code and Nick Hodges has written a good chapter on it. Reading that I have produced this code:
//interfaces
type
ILocalization = interface
['{1D144BCE-7D79-4672-8FB5-235422F712EE}']
function localize(const aWordId: string): string;
end;
type
IActions = interface
['{31E8B24F-0B17-41BC-A9E4-F93A8E7F6ECF}']
procedure addWord(const aIndex, aWord: string);
procedure removeWord(const aIndex: string);
end;
//implementation
type
TLocalization = class sealed (TInterfacedObject, ILocalization, IActions)
private
FTranslationList: TDictionary<string, string>;
public
constructor Create;
destructor Destroy; override;
//interface implementations
function localize(const aWordId: string): string;
procedure addWord(const aIndex, aWord: string);
procedure removeWord(const aIndex: string);
end;
I am planning to use this class to localize (translate) my delphi android apps.
When I am going to make an instance of the class I do the following.
var
Italiano: TLocalization;
begin
Italiano := TLocalization.Create;
end;
Since TLocalization should have inherited AddRef and Release I won't try finally this, but does this happen even if Italiano is a class type and not an interface type?
What I mean is this:
Given the fact that (as I've already said) TLocalization inherits the AddRef and Release I don't have to free if I have understood correctly. It should be the same with ILocalization but does it have other benefits? I don't understand what are the differences of the 2 cases above
var
Italiano: TLocalization;
begin
Italiano := TLocalization.Create;
// do stuff
end;
This opens you up to the pitfalls of mixing different lifetime models. As the code stands, AddRef has not been called, and so the reference count is 0. Consequently, you will leak this object.
So you might change the code to:
var
Italiano: TLocalization;
begin
Italiano := TLocalization.Create;
try
// do stuff
finally
Italiano.Free;
end;
end;
Now you don't leak.
Great. But what happens if you do take a reference?
var
Italiano: TLocalization;
Localization: ILocalization;
begin
Italiano := TLocalization.Create;
try
Localization := Italiano as ILocalization;
// do stuff
finally
Italiano.Free;
end;
end;
Now AddRef is called when you assign to Localization. So the reference count goes to 1. When Localization goes out of scope, Release is called and the reference count returns to 0 and the instance is destroyed. Unfortunately, you are also explicitly destroying it. Objects need to be destroyed exactly once.
The cleanest and simplest rule to follow is not to mix lifetime models. If you have lifetime management by reference counting, do that only. Make sure that you always take a reference to the object when you create it, and let the compiler generate reference counting code to manage the lifetime.
One way to make sure that you follow that rule is to ensure that you only ever access the object through interface references. Like this:
var
Italiano: ILocalization;
begin
Italiano := TLocalization.Create;
// do stuff
end;
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