《GOF設計模式》—觀察者(OBSERVER)—Delphi源碼示例:圖形用戶界面工具箱

示例:圖形用戶界面工具箱
說明:
許多圖形用戶界面工具箱將用戶應用的界面表示與底下的應用數據分離。定義應用數據的類和負責界面表示的類可以各自獨立地複用。當然它們也可一起工作。一個表格對象和一個柱狀圖對象可使用不同的表示形式描述同一個應用數據對象的信息。表格對象和柱狀圖對象互相併不知道對方的存在,這樣使你可以根據需要單獨複用表格或柱狀圖。但在這裏是它們表現的似乎互相知道。當用戶改變表格中的信息時,柱狀圖能立即反映這一變化,反過來也是如此。
 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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章