I read with interest Nick Hodges blog on Why You Should Be Using Interfaces and since I'm already in love with interfaces at a higher level in my coding I decided to look at how I could extend this to quite low levels and to investigate what support for this existed in the VCL classes.
A common construct that I need is to do something simple with a TStringList, for example this code to load a small text file list into a comma text string:
var
MyList : TStrings;
sCommaText : string;
begin
MyList := TStringList.Create;
try
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
finally
MyList.Free;
end;
end;
It would seem a nice simplification if I could write with using MyList as an interface - it would get rid of the try-finally and improve readability:
var
MyList : IStrings;
//^^^^^^^
sCommaText : string;
begin
MyList := TStringList.Create;
MyList.LoadFromFile( 'c:\temp\somefile.txt' );
sCommaText := MyList.CommaText;
// ... do something with sCommaText.....
end;
I can't see an IStrings defined though - certainly not in Classes.pas, although there are references to it in connection with OLE programming online. Does it exist? Is this a valid simplification? I'm using Delphi XE2.
There is no interface in the RTL/VCL that does what you want (expose the same interface as TStrings
). If you wanted to use such a thing you would need to invent it yourself.
You would implement it with a wrapper like this:
type
IStrings = interface
function Add(const S: string): Integer;
end;
TIStrings = class(TInterfacedObject, IStrings)
private
FStrings: TStrings;
public
constructor Create(Strings: TStrings);
destructor Destroy; override;
function Add(const S: string): Integer;
end;
constructor TIStrings.Create(Strings: TStrings);
begin
inherited Create;
FStrings := Strings;
end;
destructor TIStrings.Destroy;
begin
FStrings.Free; // don't use FreeAndNil because Nick might see this code ;-)
inherited;
end;
function TIStrings.Add(const S: string): Integer;
begin
Result := FStrings.Add(S);
end;
Naturally you would wrap up the rest of the TStrings
interface in a real class. Do it with a wrapper class like this so that you can wrap any type of TStrings
just by having access to an instance of it.
Use it like this:
var
MyList : IStrings;
....
MyList := TIStrings.Create(TStringList.Create);
You may prefer to add a helper function to actually do the dirty work of calling TIStrings.Create
.
Note also that lifetime could be an issue. You may want a variant of this wrapper that does not take over management of the lifetime of the underlying TStrings
instance. That could be arranged with a TIStrings
constructor parameter.
Myself, I think this to be an interesting thought experiment but not really a sensible approach to take. The TStrings
class is an abstract class which has pretty much all the benefits that interfaces offer. I see no real downsides to using it as is.
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