The goal is to create a type called TURLString to be called as follows:
var
newURl : TURLString;
begin
newURL.Append('http://').Append开发者_开发百科('www.thehost.com').Append('path/on/server').Append('?');
...lots of app logic...
newURL.AppendParam('name', 'value').Append('#').AppendParam('name', 'value');
...more params added...
result := httpClient.Get(newURL);
end;
With TURLString defined like this (note its a record):
//from actual code used
TURLString = record
private
FString : string;
public
function Append(APart : string) : TURLString;
function AppendParam(AParam, AValue : string) : TURLString;
end;
function TURLString.Append(APart: string) : TURLString;
begin
FString := FString + APart;
result := self;
end;
function TURLString.AppendParam(AParam, AValue: string): TURLString;
begin
if (not Empty) then
FString := FString + URL_AMB;
FString := FString + AParam + '=' + AValue;
result := self;
end;
When stepping through the fluid calls, the values are appended but when exiting they revert to the first string passed into the first append call and newURL is equal to 'http://' while debugging the append call you see 'http://www.thehost.com/path/on/server?name=value#name=value'.
Is this concept possible with a record?
To get rid of the performance and memory use problem of record copy, you may use pointers as results type for your methods:
type
PURLString = ^TURLString;
TURLString = record
private
FString : string;
public
function Append(const APart : string) : PURLString;
function AppendParam(const AParam, AValue : string) : PURLString;
end;
function TURLString.Append(const APart: string) : PURLString;
begin
FString := FString + APart;
result := @self;
end;
function TURLString.AppendParam(const AParam, AValue: string): PURLString;
begin
if FString <> '' then
FString := FString + URL_AMB;
FString := FString + AParam + '=' + AValue;
result := @self;
end;
So you may be able to call your original TURLString just as wished:
newURL.Append('http://').Append('www.thehost.com').Append(...
And don't forget the const
keyword for string parameters of methods.
Note that the proper way of using a pointer should have been
newURL.Append('http://')^.Append('www.thehost.com')^.Append(...
but in fact the Delphi compiler is clever enough to add the ^ sign implicitly.
With this trick, you don't have to create nor free your newURL
instance. Note that only reference-counted parameters (string, variants, interfaces) will be initialized on the stack: integers or doubles will be pure random.
This is exactly what I used for creating some RTF content in my SynProject documentation tool (at least in the first versions, now I use a class instead of a record, since I wanted to add inheritance).
The record style creates a new, anonymous (and therefore unreachable, for you) record for each call and the Result = self;
line copies the "current" record to the new one. This may not be what you want. As David says, you'll have to assign the (anonymous) end result to a record variable you declared, so you can finally access the end result.
If you use a reference type (object or interface), it will return a reference to itself, and no new object is generated (and nothing is copied). That makes a lot more sense in the fluid style.
If you use a value type like a record then you need to assign the final returned result to a variable:
newURL := newURL.Append('http://').Append('www.thehost.com');
If you use a reference type like a class instance, then you can use the syntax that you used in your question.
The reference type approach treats the data type as mutable, whereas for value types you are best implementing an immutable data type.
Just take a look at my TPathBuilder implementation here. It's main purpose is to build file paths but you can get the idea. Source code is included in the post.
精彩评论