Template Method模式
起源
Delphi的Template Method模式以Object Pascal的虚函数为基础的结构型模式。
目的
定义一组算法操作的框架,定义一些不改变算法结构的子类。
动机
· 更好地封装策略方针并分发到不同的代理。
· 更好地实现复杂算法的代码重用,基类封装不可变的部分,并让子类实现可重载的部分行为方法。
· 更好地通过子类的可扩展部分提供钩子式的操作,但template method模式只允许通过基类来调用。
UML图示:
应用
先让我们看看几个使用了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方法。
{抽象类 TStream}
TStream = class(TObject)
private
…
protected
procedure SetSize(NewSize: Longint); virtual;
public
//源类支持的原始的方法
function Read(var Buffer; Count: Longint): Longint; virtual; abstract;
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);
procedure WriteBuffer(const Buffer; Count: Longint);
// 封装了由模板方法实现的流拷算法
function CopyFrom(Source: TStream; Count: Longint): Longint;
…
end;
//具体类
TStringStream = class(TStream)
…
public
constructor Create(const AString: string);
// 具体类实现了可重载的方法
function Read(var Buffer; Count: Longint): Longint; override;
…
function Write(const Buffer; Count: Longint): Longint; override;
…
end;
---------
{ TStream } // 抽象类
…
// template方法
procedure TStream.ReadBuffer(var Buffer; Count: Longint);
begin
if (Count <> 0) and (Read(Buffer, Count) <> Count) then
raise EReadError.Create(SReadError);
end;
procedure TStream.WriteBuffer(const Buffer; Count: Longint);
begin
if (Count <> 0) and (Write(Buffer, Count) <> Count) then
raise EWriteError.Create(SWriteError);
end;
function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;
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);
WriteBuffer(Buffer^, N);
Dec(Count, N);
end;
finally
FreeMem(Buffer, BufSize);
end;
end;
---------
{ TStringStream } // 具体类
…
// 具体类实现了源始的操作
function TStringStream.Read(var Buffer; Count: Longint): Longint;
begin
Result := Length(FDataString) - FPosition;
if Result > Count then Result := Count;
Move(PChar(@FDataString[FPosition + 1])^, Buffer, Result);
Inc(FPosition, Result);
end;
function TStringStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := Count;
SetLength(FDataString, (FPosition + Result));
Move(Buffer, PChar(@FDataString[FPosition + 1])^, Result);
Inc(FPosition, Result);
end;