示例:RTF阅读器
特点:
通常情况下,由具体生成器生成的产品,它们的表示相差是如此之大以至于给不同的产品以公共父类没有太大意思。因为客户通常用合适的具体生成器来配置导向者,客户处于的位置使它知道Builder的哪一个具体子类被使用,以及能处理具体Builder生成的产品。
实现:
一个RTF(RichTextFormat)文档交换格式的阅读器应能将RTF转换为多种正文格式,如该阅读器可以将RTF文档转换成普通ASCII文本。但问题在于“格式转换”的数目可能是无限的。因此要求能够很容易的实现新的格式转换,同时却无需改变RTF阅读器的实现。
一个解决办法是用一个可以将RTF转换成另一种正文表示的“TextConverter对象”来配置“RTFReader对象”。当RTFReader对RTF文档进行语法分析时,它使用TextConverter去做转换。无论何时RTFReader识别了一个RTF标记(或是普通正文或是一个RTF控制字),它都发送一个请求给TextConverter去转换这个标记。TextConverter对象负责进行数据转换以及用特定格式表示该标记,如下图所示。
TextConvert的子类对不同转换和不同格式进行特殊处理。例如,一个ASCIIConverter只负责转换普通文本,而忽略其他转换请求。另一方面,一个TeXConverter将会为实现对所有请求的操作,以便生成一个获取正文中所有风格信息的TEX表示。一个TextWidgetConverter将生成一个复杂的用户界面对象以便用户浏览和编辑正文。
每种转换器类将创建和装配一个复杂对象的机制隐含在抽象接口的后面。转换器独立于阅读器,阅读器负责对一个RTF文档进行语法分析。
Builder模式描述了所有这些关系。每一个转换器类在该模式中被称为生成器(builder),而阅读器则称为导向器(director)。在上面的例子中,Builder模式将分析文本格式的算法(即RTF文档的语法分析程序)与描述怎样创建和表示一个转换后格式的算法分离开来。这使我们可以重用RTFReader的语法分析算法,根据RTF文档创建不同的正文表示——仅需使用不同的TextConverter的子类配置该RTFReader即可。
代码:
unit uRTFReader;
interface
uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs;
type
TTokenType = (ttCHAR,ttFONT,ttPARA);
TToken = record
FType: TTokenType;
FChar: Char;
FFont: string;
end;
PToken = ^TToken;
TTokenIterator = class;
TTokenList = class
private
FDataList: TList;
function GetCount: Integer;
function GetItems(Index: integer): PToken;
public
constructor Create;
destructor Destroy; override;
//---
function Add(const Token: TToken): Boolean;
procedure Clear;
function CreateIterator: TTokenIterator;
//---
property Count: Integer read GetCount;
property Items[Index: integer]: PToken read GetItems;default;
end;
TTokenIterator = class
private
FTokens: TTokenList;
FCurIndex: integer;
function GetToken: PToken;
public
constructor Create(Tokens: TTokenList);
//---
procedure First;
procedure Last;
procedure Next;
procedure Prev;
//---
function BOF: Boolean;
function EOF: Boolean;
//--
property Token: PToken read GetToken;
end;
TASCIIText = class
private
FText: string;
public
property Text: string read FText write FText;
end;
TTeXText = class
end;
TTextWidget = class
end;
TTextConverter = class
public
procedure ConvertCharacter(AChar: Char); virtual;
procedure ConvertFontChange(Font: string); virtual;
procedure ConvertParagraph(); virtual;
end;
TASCIIConverter = class(TTextConverter)
private
FASCIIText: TASCIIText;
public
constructor Create;
//---
procedure ConvertCharacter(AChar: Char); override;
function GetASCIIText: TASCIIText;
end;
TTeXConverter = class(TTextConverter)
private
FTeXText: TTeXText;
public
constructor Create;
//---
procedure ConvertCharacter(AChar: Char); override;
procedure ConvertFontChange(Font: string); override;
procedure ConvertParagraph; override;
function GetTeXText: TTeXText;
end;
TTextWidgetConverter = class(TTextConverter)
private
FTextWidget: TTextWidget;
public
constructor Create;
//---
procedure ConvertCharacter(AChar: Char); override;
procedure ConvertFontChange(Font: string); override;
procedure ConvertParagraph; override;
function GetTextWidget: TTextWidget;
end;
TRTFReader = class
private
FTokens: TTokenList;
public
constructor Create(Tokens: TTokenList);
//---
procedure ParseRTF(Builder: TTextConverter);
end;
implementation
procedure TTextConverter.ConvertCharacter(AChar: Char);
begin
end;
procedure TTextConverter.ConvertFontChange(Font: string);
begin
end;
procedure TTextConverter.ConvertParagraph;
begin
end;
constructor TASCIIConverter.Create;
begin
FASCIIText := nil;
end;
procedure TASCIIConverter.ConvertCharacter(AChar: Char);
begin
if FASCIIText = nil then
FASCIIText := TASCIIText.Create;
//---
FASCIIText.Text := FASCIIText.Text + AChar;
end;
function TASCIIConverter.GetASCIIText: TASCIIText;
begin
Result := FASCIIText;
end;
constructor TTeXConverter.Create;
begin
FTeXText := nil;
end;
procedure TTeXConverter.ConvertCharacter(AChar: Char);
begin
end;
procedure TTeXConverter.ConvertFontChange(Font: string);
begin
end;
procedure TTeXConverter.ConvertParagraph;
begin
end;
function TTeXConverter.GetTeXText: TTeXText;
begin
Result := FTeXText;
end;
constructor TTextWidgetConverter.Create;
begin
FTextWidget := nil;
end;
procedure TTextWidgetConverter.ConvertCharacter(AChar: Char);
begin
end;
procedure TTextWidgetConverter.ConvertFontChange(Font: string);
begin
end;
procedure TTextWidgetConverter.ConvertParagraph;
begin
end;
function TTextWidgetConverter.GetTextWidget: TTextWidget;
begin
Result := FTextWidget;
end;
constructor TRTFReader.Create(Tokens: TTokenList);
begin
FTokens := Tokens;
end;
procedure TRTFReader.ParseRTF(Builder: TTextConverter);
var
Iterator: TTokenIterator;
begin
Iterator := FTokens.CreateIterator;
try
with Iterator do
begin
First;
while not EOF do
begin
case Token.FType of
ttCHAR: Builder.ConvertCharacter(Token.FChar);
ttFONT: Builder.ConvertFontChange(Token.FFont);
ttPARA: Builder.ConvertParagraph;
end;
//---
next;
end;
end;
finally
Iterator.Free;
end;
end;
constructor TTokenList.Create;
begin
inherited;
//---
FDataList := TList.Create;
end;
destructor TTokenList.Destroy;
begin
Clear;
FDataList.Free;
//---
inherited;
end;
procedure TTokenList.Clear;
var
i: Integer;
pData: PToken;
begin
with FDataList do
begin
for i := 0 to Count - 1 do
begin
pData := Items[i];
Dispose(pData);
end;
Clear;
end;
end;
function TTokenList.Add(const Token: TToken): Boolean;
var
pData: PToken;
begin
new(pData);
pData^ := Token;
FDataList.Add(pData);
end;
function TTokenList.GetCount: Integer;
begin
Result := FDataList.Count;
end;
function TTokenList.GetItems(Index: integer): PToken;
begin
Result := FDataList[Index];
end;
constructor TTokenIterator.Create(Tokens: TTokenList);
begin
FTokens := Tokens;
FCurIndex := 0;
end;
function TTokenIterator.BOF: Boolean;
begin
Result := FCurIndex <= 0;
end;
function TTokenIterator.EOF: Boolean;
begin
Result := FCurIndex >= FTokens.Count;
end;
procedure TTokenIterator.First;
begin
FCurIndex := 0;
end;
function TTokenIterator.GetToken: PToken;
begin
Result := FTokens[FCurIndex];
end;
procedure TTokenIterator.Last;
begin
FCurIndex := FTokens.Count - 1;
end;
procedure TTokenIterator.Next;
begin
FCurIndex := FCurIndex + 1;
end;
procedure TTokenIterator.Prev;
begin
FCurIndex := FCurIndex - 1;
end;
function TTokenList.CreateIterator: TTokenIterator;
begin
Result := TTokenIterator.Create(self);
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
var
Tokens: TTokenList;
Reader: TRTFReader;
Converter: TASCIIConverter;
ASCIIText: TASCIIText;
//---
procedure _AddToken;
var
Token: TToken;
begin
with Token do
begin
FType := ttCHAR;
FChar := 'A';
FFont := '';
end;
Tokens.Add(Token);
//---
with Token do
begin
FType := ttFONT;
FChar := ' ';
FFont := '宋体 9';
end;
Tokens.Add(Token);
//---
with Token do
begin
FType := ttPARA;
FChar := ' ';
FFont := '';
end;
Tokens.Add(Token);
with Token do
begin
FType := ttCHAR;
FChar := 'B';
FFont := '';
end;
Tokens.Add(Token);
end;
begin
Tokens := TTokenList.Create;
Reader := TRTFReader.Create(Tokens);
Converter := TASCIIConverter.Create;
//--
_AddToken;
Reader.ParseRTF(Converter);
ASCIIText := Converter.GetASCIIText;
showmessage(ASCIIText.Text);
//---
ASCIIText.Free;
Converter.Free;
Reader.Free;
Tokens.Free;
end;