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

来源:互联网 发布:mac版qq查看群相册 编辑:程序博客网 时间:2024/03/29 09:22

示例:图形用户界面工具箱
说明:
许多图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离。定义应用数据的类和负责界面表示的类可以各自独立地复用。当然它们也可一起工作。一个表格对象和一个柱状图对象可使用不同的表示形式描述同一个应用数据对象的信息。表格对象和柱状图对象互相并不知道对方的存在,这样使你可以根据需要单独复用表格或柱状图。但在这里是它们表现的似乎互相知道。当用户改变表格中的信息时,柱状图能立即反映这一变化,反过来也是如此。
 clip_image002
这一行为意味着表格对象和棒状图对象都依赖于数据对象,因此数据对象的任何状态改变都应立即通知它们。同时也没有理由将依赖于该数据对象的对象的数目限定为两个,对相同的数据可以有任意数目的不同用户界面。
Observer模式描述了如何建立这种关系。

界面:
 clip_image002[5]
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


代码:
 

clip_image002[7]


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.

原创粉丝点击