起源<o:p></o:p>
Delphi的Template Method模式以Object Pascal的虚函数为基础的结构型模式。
目的
<o:p></o:p>
定义一组算法操作的框架,定义一些不改变算法结构的子类。<o:p></o:p>
动机<o:p></o:p>
· 更好地封装策略方针并分发到不同的代理。
· 更好地实现复杂算法的代码重用,基类封装不可变的部分,并让子类实现可重载的部分行为方法。
· 更好地通过子类的可扩展部分提供钩子式的操作,但template method模式只允许通过基类来调用。<o:p></o:p>
<o:p></o:p>
应用<o:p></o:p>
先让我们看看几个使用了template method模式VCL组件。在下例中,VCL组件中的抽象类Tstream(定义于classes.pas)实现了流拷贝方法CopyFrom(),template 方法CopyFrom()包含了流拷贝的必需的算法。TStream 将Read()和Write()方法声名为抽象方法,并将实现延期到其具体的子类。CopyFrom()通过ReadBuffer()、WriteBuffer() 访问Read()、Write(),
ReadBuffer()、WriteBuffer()为模板方法。以后它们将提供静态并简单Read()和Write()虚方法接口。Read() 和 Write()为指定的扩展点并在派生类中执行
更的详细实现代码请参阅VCL的classes.pas单元的,抽象类Tstream和具体的流类:TCustomMemoryStream/TmemoryStream及TstringStream。通常template方法在基类中被定义为静态,并且派生的操作定义为虚方法,以后你只需要在子类中重载扩展点。对了,接口不支持template方法。
<o:p></o:p>
{抽象类 TStream}
<o:p></o:p>
TStream = class(TObject)
private
…
protected
procedure SetSize(NewSize: Longint); virtual;
public
<o:p></o:p>
//源类支持的原始的方法<o:p></o:p>
function Read(var Buffer; Count: Longint): Longint; virtual; abstract;<o:p></o:p>
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;
// template方法
procedure ReadBuffer(var Buffer; Count: Longint);<o:p></o:p>
procedure WriteBuffer(const Buffer; Count: Longint);<o:p></o:p>
<o:p></o:p>
// 封装了由模板方法实现的流拷算法
function CopyFrom(Source: TStream; Count: Longint): Longint;<o:p></o:p>
…
end;
<o:p></o:p>
<o:p></o:p>
<o:p></o:p>
//具体类
TStringStream = class(TStream)
…
public
constructor Create(const AString: string);
// 具体类实现了可重载的方法
function Read(var Buffer; Count: Longint): Longint; override;<o:p></o:p>
…<o:p></o:p>
function Write(const Buffer; Count: Longint): Longint; override;<o:p></o:p>
…
end;
<o:p></o:p>
<o:p></o:p>
---------
<o:p></o:p>
{ TStream } // 抽象类
…
// template方法<o:p></o:p>
procedure TStream.ReadBuffer(var Buffer; Count: Longint);<o:p></o:p>
begin<o:p></o:p>
if (Count <> 0) and (Read(Buffer, Count) <> Count) then
raise EReadError.Create(SReadError);
end;<o:p></o:p>
<o:p></o:p>
procedure TStream.WriteBuffer(const Buffer; Count: Longint);<o:p></o:p>
begin<o:p></o:p>
if (Count <> 0) and (Write(Buffer, Count) <> Count) then
raise EWriteError.Create(SWriteError);
end;
<o:p></o:p>
function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;<o:p></o:p>
const
MaxBufSize = $F000;
var
BufSize, N: Integer;
Buffer: PChar;
begin
if Count = 0 then
begin
Source.Position := 0;
Count := Source.Size;
end;
Result := Count;
if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;
GetMem(Buffer, BufSize);
try
while Count <> 0 do
begin
if Count > BufSize then N := BufSize else N := Count;
Source.ReadBuffer(Buffer^, N);<o:p></o:p>
WriteBuffer(Buffer^, N);<o:p></o:p>
Dec(Count, N);
end;
finally
FreeMem(Buffer, BufSize);
end;
end;
<o:p></o:p>
<o:p></o:p>
---------
{ TStringStream } // 具体类
…
// 具体类实现了源始的操作
function TStringStream.Read(var Buffer; Count: Longint): Longint;<o:p></o:p>
begin
Result := Length(FDataString) - FPosition;
if Result > Count then Result := Count;
Move(PChar(@FDataString[FPosition + 1])^, Buffer, Result);
Inc(FPosition, Result);
end;
<o:p></o:p>
function TStringStream.Write(const Buffer; Count: Longint): Longint;<o:p></o:p>
begin
Result := Count;
SetLength(FDataString, (FPosition + Result));
Move(Buffer, PChar(@FDataString[FPosition + 1])^, Result);
Inc(FPosition, Result);
end;
<o:p></o:p>