《GOF设计模式》—观察者(OBSERVER)—Delphi源码示例:图形用户界面工具箱

    技术2022-05-19  23

    示例:图形用户界面工具箱 说明: 许多图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离。定义应用数据的类和负责界面表示的类可以各自独立地复用。当然它们也可一起工作。一个表格对象和一个柱状图对象可使用不同的表示形式描述同一个应用数据对象的信息。表格对象和柱状图对象互相并不知道对方的存在,这样使你可以根据需要单独复用表格或柱状图。但在这里是它们表现的似乎互相知道。当用户改变表格中的信息时,柱状图能立即反映这一变化,反过来也是如此。   这一行为意味着表格对象和棒状图对象都依赖于数据对象,因此数据对象的任何状态改变都应立即通知它们。同时也没有理由将依赖于该数据对象的对象的数目限定为两个,对相同的数据可以有任意数目的不同用户界面。 Observer模式描述了如何建立这种关系。 界面:   object Form3: TForm3   Left = 192   Top = 110   Width = 371   Height = 294   Caption = 'Form3'   Color = clBtnFace   Font.Charset = DEFAULT_CHARSET   Font.Color = clWindowText   Font.Height = -11   Font.Name = 'MS Sans Serif'   Font.Style = []   OldCreateOrder = False   OnCreate = FormCreate   OnDestroy = FormDestroy   PixelsPerInch = 96   TextHeight = 13   object Button1: TButton     Left = 56     Top = 224     Width = 75     Height = 25     Caption = 'Button1'     TabOrder = 0     OnClick = Button1Click   end   object Button2: TButton     Left = 152     Top = 224     Width = 75     Height = 25     Caption = 'Button2'     TabOrder = 1     OnClick = Button2Click   end end 代码:  

    unit uView; interface uses     Windows, SysUtils, Classes, Graphics, Controls,      Math; type     TDataModel = class;     TView = class(TGraphicControl)     public         procedure Update1; virtual; abstract;     end;     TDataView = class(TView)     private         FModel: TDataModel;     public         constructor Create(AOwner: TComponent; AModel: TDataModel); reintroduce;         destructor Destroy; override;         //---         procedure Update1; override;     end;     TTableView = class(TDataView)     protected         procedure Paint; override;     end;     TStickView = class(TDataView)     protected         procedure Paint; override;     end;     TPieView = class(TDataView)     protected         procedure Paint; override;     end;     TModel = class     private         FObservers: TList;     public         constructor Create;         destructor Destroy; override;         //---         procedure Attach(AObserver: TView);         procedure Detach(AObserver: TView);         procedure Notify();     end;     RData = record         x, y, z: Integer;     end;     TDataModel = class(TModel)     private         FA: RData;         FB: RData;         FC: RData;     public         property A: RData read FA write FA;         property B: RData read FB write FB;         property C: RData read FC write FC;     end; function GetData(AX, AY, AZ: Integer): RData; implementation function GetData(AX, AY, AZ: Integer): RData; begin     with Result do     begin         x := AX;         y := AY;         z := AZ;     end; end; constructor TModel.Create; begin     inherited;     //---     FObservers := TList.Create; end; destructor TModel.Destroy; begin     FObservers.Free;     //---     inherited; end; procedure TModel.Attach(AObserver: TView); begin     FObservers.Add(AObserver); end; procedure TModel.Detach(AObserver: TView); begin     FObservers.Remove(AObserver); end; procedure TModel.Notify(); var     i: Integer; begin     with FObservers do     begin         for i := 0 to Count - 1 do             TView(Items[i]).Update1;     end; end; procedure TTableView.Paint;     //---     procedure _ClearRect;     begin         with self.Canvas do         begin             with Brush do             begin                 Color := clBlack;                 Style := bsSolid;             end;             FillRect(self.ClientRect);         end;     end;     //---     procedure _DrawTable;     const         CNT_RowCount = 4;         CNT_ColCount = 4;     var         ARect: TRect;         ARowHeight, AColWidth: Integer;         //---         procedure _GetRect;         const             CNT_Size = 5;         begin             ARect := self.ClientRect;             with ARect do             begin                 Left := Left + CNT_Size;                 Right := Right - CNT_Size;                 Top := Top + CNT_Size;                 Bottom := Bottom - CNT_Size;             end;         end;         //---         procedure _GetAxis;         begin             with ARect do             begin                 AColWidth := (Right - Left) div CNT_ColCount;                 ARowHeight := (Bottom - Top) div CNT_RowCount;             end;         end;         //---         function _GetX(ACol: Integer): Integer;         begin             with ARect do                 Result := Left + AColWidth * ACol;         end;         //---         function _GetY(ARow: Integer): Integer;         begin             with ARect do                 Result := Top + ARowHeight * ARow;         end;         //---         procedure _DrawGrid;         var             i, X, Y: Integer;         begin             with self.Canvas do             begin                 with Pen do                 begin                     Color := clYellow;                     Width := 1;                     Style := psSolid;                 end;                 Rectangle(ARect);                 //---                 with ARect do                 begin                     for i := 1 to CNT_RowCount - 1 do                     begin                         Y := _GetY(i);                         MoveTo(Left, Y);                         LineTo(Right, Y);                     end;                     //---                     for i := 1 to CNT_ColCount - 1 do                     begin                         X := _GetX(i);                         MoveTo(X, Top);                         LineTo(X, Bottom);                     end;                 end;             end;         end;         //---         procedure _DrawData;             //---             procedure _DrawColDatas(ACol: Integer; const ADatas: array of string);             var                 X, ARow: Integer;             begin                 X := _GetX(ACol);                 with self.Canvas do                 begin                     for ARow := Low(ADatas) to High(ADatas) do                         TextOut(X + 2, _GetY(ARow) + 2, ADatas[ARow]);                 end;             end;         begin             with self.Canvas do             begin                 with Font do                 begin                     Color := clYellow;                     Size := 10;                 end;             end;             //---             with FModel do             begin                 _DrawColDatas(0, ['', 'x', 'y', 'z']);                 with A do                     _DrawColDatas(1, ['a', IntToStr(x), IntToStr(y), IntToStr(z)]);                 with B do                     _DrawColDatas(2, ['b', IntToStr(x), IntToStr(y), IntToStr(z)]);                 with C do                     _DrawColDatas(3, ['c', IntToStr(x), IntToStr(y), IntToStr(z)]);             end;         end;     begin         _GetRect;         _GetAxis;         _DrawGrid;         _DrawData;     end; begin     _ClearRect;     _DrawTable; end; constructor TDataView.Create(AOwner: TComponent; AModel: TDataModel); begin     inherited Create(AOwner);     //---     FModel := AModel;     FModel.Attach(self); end; destructor TDataView.Destroy; begin     FModel.Detach(self);     //---     inherited; end; procedure TDataView.Update1; begin     self.Paint; end; procedure TStickView.Paint;     //---     procedure _ClearRect;     begin         with self.Canvas do         begin             with Brush do             begin                 Color := clBlack;                 Style := bsSolid;             end;             FillRect(self.ClientRect);         end;     end;     //---     procedure _DrawView;     const         CNT_ColCount = 3;     var         ARect: TRect;         AValueHeight, AColWidth: Integer;         //---         procedure _GetRect;         const             CNT_Size = 5;         begin             ARect := self.ClientRect;             with ARect do             begin                 Left := Left + CNT_Size;                 Right := Right - CNT_Size;                 Top := Top + CNT_Size;                 Bottom := Bottom - CNT_Size * 4;             end;         end;         //---         procedure _GetAxis;         var             AMaxValue: Integer;         begin             with ARect do             begin                 AColWidth := (Right - Left) div CNT_ColCount;                 //---                 with FModel do                 begin                     AMaxValue := A.y;                     with B do                         if AMaxValue < y then                             AMaxValue := y;                     with C do                         if AMaxValue < y then                             AMaxValue := y;                     AMaxValue := Trunc(AMaxValue * 1.2);                 end;                 //---                 if AMaxValue > 0 then                     AValueHeight := (Bottom - Top) div AMaxValue                 else                     AValueHeight := (Bottom - Top);             end;         end;         //---         function _GetX(ACol: Integer): Integer;         begin             with ARect do                 Result := Left + trunc(AColWidth * (ACol + 0.5));         end;         //---         function _GetY(AValue: Integer): Integer;         begin             with ARect do                 Result := Bottom - AValueHeight * AValue;         end;         //---         procedure _DrawAxis;         begin             with self.Canvas do             begin                 with Pen do                 begin                     Color := clYellow;                     Width := 1;                     Style := psSolid;                 end;                 //---                 with ARect do                 begin                     MoveTo(Left, Bottom);                     LineTo(Right, Bottom);                     //---                     MoveTo(Left, Top);                     LineTo(Left, Bottom);                 end;             end;         end;         //---         procedure _DrawData;             //---             procedure _DrawColData(ACol: Integer; const ALable: string; const AValue: Integer);             var                 X, AWidth: Integer;             begin                 X := _GetX(ACol);                 with self.Canvas do                 begin                     Brush.Style := bsClear;                     TextOut(X, ARect.Bottom + 2, ALable);                     //---                     if AValue > 0 then                     begin                         with Brush do                         begin                             Color := clYellow;                             Style := bsSolid;                         end;                         //---                         AWidth := AColWidth div 4;                         Rectangle(X - AWidth, _GetY(AValue), X + AWidth, ARect.Bottom);                     end;                 end;             end;         begin             with self.Canvas do             begin                 with Font do                 begin                     Color := clYellow;                     Size := 10;                 end;             end;             //---             with FModel do             begin                 _DrawColData(0, 'a', A.y);                 _DrawColData(1, 'b', B.y);                 _DrawColData(2, 'c', C.y);             end;         end;     begin         _GetRect;         _GetAxis;         _DrawAxis;         _DrawData;     end; begin     _ClearRect;     _DrawView; end; procedure TPieView.Paint;     //---     procedure _ClearRect;     begin         with self.Canvas do         begin             with Brush do             begin                 Color := clBlack;                 Style := bsSolid;             end;             FillRect(self.ClientRect);         end;     end;     //---     procedure _DrawView;     var         ARect: TRect;         R, SumValue: Integer;         P: TPoint;         //---         procedure _GetRect;         const             CNT_Size = 5;         begin             ARect := self.ClientRect;             with ARect do             begin                 Left := Left + CNT_Size;                 Right := Right - CNT_Size;                 Top := Top + CNT_Size;                 Bottom := Bottom - CNT_Size;             end;         end;         //---         procedure _GetAxis;         begin             with ARect do             begin                 R := Min(Right - Left, Bottom - Top) div 2;                 P.X := (Left + Right) div 2;                 P.Y := (Top + Bottom) div 2;             end;             //---             with FModel do                 SumValue := A.y + B.y + C.y;         end;         //---         function _GetRadian(AValue: Integer): double;         begin             Result := 2 * Pi * (AValue / SumValue);         end;         //---         function _GetX(AValue, ARadius: Integer): Integer;         begin             Result := P.X + trunc(ARadius * cos(_GetRadian(AValue)));         end;         //---         function _GetY(AValue, ARadius: Integer): Integer;         begin             Result := P.Y - trunc(ARadius * sin(_GetRadian(AValue)));         end;         //---         procedure _DrawAxis(const AValues: array of Integer);         var             X, Y, AValue, i: Integer;         begin             with self.Canvas do             begin                 with Pen do                 begin                     Color := clYellow;                     Width := 1;                     Style := psSolid;                 end;                 //---                 Ellipse(P.X - R, P.Y - R, P.X + R, P.Y + R);                 //---                 MoveTo(P.X, P.Y);                 LineTo(P.X + R, P.Y);                 //---                 AValue := 0;                 for i := Low(AValues) to High(AValues) do                 begin                     AValue := AValue + AValues[i];                     //---                     X := _GetX(AValue, R);                     Y := _GetY(AValue, R);                     //---                     MoveTo(P.X, P.Y);                     LineTo(X, Y);                 end;             end;         end;         //---         procedure _DrawData(const AValues: array of Integer; const ALables: array of string);         var             X, Y, AValue, ASumValue, ARadius, i: Integer;         begin             with self.Canvas do             begin                 with Font do                 begin                     Color := clYellow;                     Size := 10;                 end;                 Brush.Style := bsClear;                 //---                 ARadius := R div 2;                 ASumValue := 0;                 for i := Low(AValues) to High(AValues) do                 begin                     AValue := ASumValue + AValues[i] div 2;                     ASumValue := ASumValue + AValues[i];                     //---                     X := _GetX(AValue, ARadius);                     Y := _GetY(AValue, ARadius);                     //---                     with TextExtent(ALables[i]) do                         TextOut(X - cX div 2, Y - cY div 2, ALables[i]);                 end;             end;         end;     begin         _GetRect;         _GetAxis;         with FModel do         begin             if SumValue > 0 then             begin                 _DrawAxis([A.y, B.y]);                 _DrawData([A.y, B.y, C.y], ['a', 'b', 'c']);             end;         end;     end; begin     _ClearRect;     _DrawView; end; end. unit Unit3; interface uses     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,     Dialogs, StdCtrls, uView; type     TForm3 = class(TForm)         Button1: TButton;         Button2: TButton;         procedure Button1Click(Sender: TObject);         procedure Button2Click(Sender: TObject);         procedure FormDestroy(Sender: TObject);         procedure FormCreate(Sender: TObject);     private         FModel: TDataModel;         FTableView: TTableView;         FStickView: TStickView;         FPieView:TPieView;     public     { Public declarations }     end; var     Form3: TForm3; implementation {$R *.dfm} procedure TForm3.FormCreate(Sender: TObject); begin     FModel := TDataModel.Create;     //---     FTableView := TTableView.Create(nil, FModel);     with FTableView do     begin         Parent := self;         Top := 10;         Left := 10;         Width := 100;         Height := 100;     end;     //---     FStickView := TStickView.Create(nil, FModel);     with FStickView do     begin         Parent := self;         Top := 10;         Left := 120;         Width := 100;         Height := 100;     end;     //---     FPieView := TPieView.Create(nil, FModel);     with FPieView do     begin         Parent := self;         Top := 10;         Left := 230;         Width := 100;         Height := 100;     end; end; procedure TForm3.FormDestroy(Sender: TObject); begin     FTableView.Free;     FStickView.Free;     FPieView.Free;     FModel.Free; end; procedure TForm3.Button1Click(Sender: TObject); begin     with FModel do     begin         A := GetData(60, 50, 80);         B := GetData(30, 30, 10);         C := GetData(10, 20, 10);         //---         Notify;     end; end; procedure TForm3.Button2Click(Sender: TObject); begin     with FModel do     begin         A := GetData(60, 20, 80);         B := GetData(30, 40, 10);         C := GetData(10, 40, 10);         //---         Notify;     end; end; end.


    最新回复(0)