示例:使用doesNotUnderstand的Proxy
说明:
在Smalltalk中,你可以定义超类为nil的类,同时定义doesNotUnderstand方法处理消息,这样构建一些通用的代理。
在以下程序中我们假设代理有一个realSubject方法,该方法返回它的实体。在ImageProxy中,该方法将检查是否已创建了Image,并在必要的时候创建它,最后返回Image。它使用perform来执行被保留在实体中的那些消息。
doesNotUnderstand的参数是Message的一个实例,它表示代理不能理解的消息。所以,代理在转发消息给实体之前,首先确定实体的存在性,并由此对所有的消息做出响应。
doesNotUnderstand的一个优点是它可以执行任意的处理过程。例如,我们可以用这样的方式生成一个protection proxy,即指定一个可以接受的消息的集合legalMessages,这个方法在向实体转发一个消息之前,检查它的合法性。如果不是合法的,那么提示error。
代码:
unit uDocument1;
interface
uses
Windows,Messages,Classes,Graphics,Contnrs;
const
CM_ImageError = WM_USER + 1;
CM_ImageDraw = WM_USER + 2;
CM_ImageExtent = WM_USER + 3;
legalMessages: array[0..1] of integer = (CM_ImageDraw,CM_ImageExtent);
type
TImage1 = class(TObject)
private
FFileName: string;
FBitmap: TBitmap;
public
constructor Create(const AImageFile: string);
destructor Destroy; override;
//---
procedure Draw(var AMessage: TMessage);
procedure GetExtent(var AMessage: TMessage);
//---
procedure Perform(var AMessage: TMessage);
end;
TImageProxy = class
private
FImage: TImage1;
FExtent: TPoint;
FFileName: string;
function RealSubject: TImage1;
protected
procedure Error(const AMsg: string);
procedure DoesNotUnderstand(var AMessage: TMessage);
public
constructor Create(const AImageFile: string);
destructor Destroy; override;
//---
procedure Dispatch(var Message); override;
end;
TTextDocument = class
private
FCanvas: TCanvas;
FGraphics: TObjectList;
public
constructor Create(const ACanvas: TCanvas);
destructor Destroy; override;
//---
procedure Insert(aGraphic: TObject);
procedure Draw;
//---
property Canvas: TCanvas read FCanvas;
end;
implementation
constructor TImage1.Create(const AImageFile: string);
begin
inherited Create;
//---
FFileName := AImageFile;
//---
FBitmap := TBitmap.Create;
FBitmap.LoadFromFile(FFileName);
end;
destructor TImage1.Destroy;
begin
if assigned(FBitmap) then
FBitmap.Free;
//---
inherited;
end;
procedure TImage1.Draw(var AMessage: TMessage);
begin
with AMessage do
begin
TTextDocument(WParam).Canvas.Draw(LParamLo,LParamHi,FBitmap);
Result := 1;
end;
end;
procedure TImage1.GetExtent(var AMessage: TMessage);
begin
with AMessage do
begin
LParamLo := FBitmap.Width;
LParamHi := FBitmap.Height;
Result := 1;
end;
end;
procedure TImage1.Perform(var AMessage: TMessage);
begin
case AMessage.Msg of
CM_ImageDraw: Draw(AMessage);
CM_ImageExtent: GetExtent(AMessage);
end;
end;
function TImageProxy.RealSubject: TImage1;
begin
if FImage = nil then
FImage := TImage1.Create(FFileName);
result := FImage;
end;
constructor TImageProxy.Create(const AImageFile: string);
begin
inherited Create;
//---
FFileName := AImageFile;
FExtent := Point(0,0);
FImage := nil;
end;
destructor TImageProxy.Destroy;
begin
if Assigned(FImage) then
FImage.Free;
//---
inherited
end;
constructor TTextDocument.Create(const ACanvas: TCanvas);
begin
FGraphics := TObjectList.Create;
FCanvas := ACanvas;
end;
destructor TTextDocument.Destroy;
begin
FGraphics.Free;
//---
inherited;
end;
procedure TTextDocument.Draw;
var
at: TPoint;
i: integer;
//---
procedure _Draw(AGraphic: TObject);
var
AMessage: TMessage;
begin
with AMessage do
begin
Msg := CM_ImageDraw;
WParam := Integer(Self);
LParamLo := at.X;
LParamHi := at.Y;
end;
AGraphic.Dispatch(AMessage);
end;
//---
function _GetExtent(AGraphic: TObject): TPoint;
var
AMessage: TMessage;
begin
with AMessage do
begin
Msg := CM_ImageExtent;
WParam := Integer(Self);
end;
AGraphic.Dispatch(AMessage);
//---
Result := Point(AMessage.LParamLo,AMessage.LParamHi);
end;
begin
at := Point(0,0);
//---
with FGraphics do
begin
for i := 0 to Count - 1 do
begin
_Draw(Items[i]);
at.Y := at.Y + _GetExtent(Items[i]).Y;
end;
end;
end;
procedure TTextDocument.Insert(aGraphic: TObject);
begin
FGraphics.Add(aGraphic);
end;
procedure TImageProxy.Dispatch(var Message);
begin
DoesNotUnderstand(TMessage(Message));
if TMessage(Message).Result = 0 then
inherited;
end;
procedure TImageProxy.DoesNotUnderstand(var AMessage: TMessage);
//---
function _InlegalMessages: boolean;
var
i: integer;
begin
for i := low(legalMessages) to high(legalMessages) do
begin
if legalMessages[i] = AMessage.Msg then
begin
Result := true;
exit;
end;
end;
//---
Result := false;
end;
begin
if _InlegalMessages then
self.RealSubject.Perform(AMessage)
else if AMessage.Msg = CM_ImageError then
begin
//…………
AMessage.Result := 1;
end
else
self.Error('Illegal operator');
end;
procedure TImageProxy.Error(const AMsg: string);
var
AMessage: TMessage;
begin
with AMessage do
begin
Msg := CM_ImageError;
WParam := Integer(AMsg);
end;
self.Dispatch(AMessage);
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
var
ADocument: TTextDocument;
begin
ADocument := TTextDocument.Create(self.Canvas);
try
ADocument.Insert(TImageProxy.Create('D:/temp/test.bmp'));
ADocument.Insert(TImageProxy.Create('D:/temp/test1.bmp'));
ADocument.Draw;
finally
ADocument.Free;
end;
end;