《GOF设计模式》—观察者(OBSERVER)—Delphi源码示例:显式地指定感兴趣的改变

来源:互联网 发布:编程四大魔道天才 编辑:程序博客网 时间:2024/04/28 08:52

示例:显式地指定感兴趣的改变
说明:
你可以扩展目标的注册接口,让各观察者注册为仅对特定事件感兴趣,以提高更新的效率。当一个事件发生时,目标仅通知那些已注册为对该事件感兴趣的观察者。

代码:
 
clip_image002
unit uAspects;

interface

uses
    SysUtils, Classes, Dialogs;

type
    TAspect = (apCalcEvent1,apCalcEvent2);

    TObserver = class;

    RMapInfo = record
        Interest:TAspect;
        Observer: TObserver;
    end;
    PMapInfo = ^RMapInfo;

    TMapList = class(TList)
    private
        function GetItems(Index: Integer): PMapInfo;
    protected
        procedure Notify(Ptr: Pointer; Action: TListNotification); override;
    public
        function Add(AInterest: TAspect; AObserver: TObserver): Integer;
        function IndexOf(AInterest: TAspect; AObserver: TObserver): Integer;
        procedure Delete(AInterest: TAspect; AObserver: TObserver);
        //---
        property Items[Index: Integer]: PMapInfo read GetItems;
    end;

    TSubject = class
    private
        FObservers: TMapList;
        FInterest:TAspect;
    public
        constructor Create;
        destructor Destroy; override;
        //---
        procedure Attach(AObserver: TObserver;AInterest:TAspect);
        procedure Detach(AObserver: TObserver;AInterest:TAspect);
        procedure Notify;
    end;

    TConcreteSubject = class(TSubject)
    private
        FState: Integer;
    public
        procedure Calc1;
        procedure Calc2;
        //---
        property State: Integer read FState;
    end;

    TObserver = class
    public
        procedure Update(ASubject: TSubject;AInterest:TAspect); virtual; abstract;
    end;

    TConcreteObserver = class(TObserver)
    public
        procedure Update(ASubject: TSubject;AInterest:TAspect); override;
    end;

procedure Test;

implementation

procedure Test;
var
    ASubject: TConcreteSubject;
    AObserver: TObserver;
begin
    ASubject := TConcreteSubject.Create;
    AObserver := TConcreteObserver.Create;
    try
        ASubject.Attach(AObserver,apCalcEvent2);
        ASubject.Calc1;
        ASubject.Calc2;
    finally
        AObserver.Free;
        ASubject.Free;
    end;
end;

constructor TSubject.Create;
begin
    FObservers := TMapList.Create;
end;

destructor TSubject.Destroy;
begin
    FObservers.Free;
    //---
    inherited;
end;

procedure TSubject.Attach(AObserver: TObserver;AInterest:TAspect);
begin
    FObservers.Add(AInterest,AObserver);
end;

procedure TSubject.Detach(AObserver: TObserver;AInterest:TAspect);
begin
    FObservers.Delete(AInterest,AObserver);
end;

procedure TSubject.Notify;
var
    i: integer;
begin
    with FObservers do
    begin
        for i := 0 to Count - 1 do
        begin
            with Items[i]^ do
            begin
                if Interest = FInterest then
                    Observer.Update(self,Interest);
            end;
        end;
    end;
end;

procedure TConcreteObserver.Update(ASubject: TSubject;AInterest:TAspect);
begin
    if ASubject is TConcreteSubject then
    begin
        with TConcreteSubject(ASubject) do
            ShowMessage(IntToStr(State));
    end;
end;

function TMapList.Add(AInterest: TAspect; AObserver: TObserver): Integer;
var
    pData: PMapInfo;
begin
    New(pData);
    pData.Interest := AInterest;
    pData.Observer := AObserver;
    //---
    Result := inherited Add(pData);
end;

procedure TMapList.Delete(AInterest: TAspect; AObserver: TObserver);
var
    AIndex:Integer;
begin
    AIndex := IndexOf(AInterest, AObserver);
    if AIndex >= 0 then
        inherited Delete(AIndex);
end;

function TMapList.GetItems(Index: Integer): PMapInfo;
begin
    Result := inherited Items[Index];
end;

function TMapList.IndexOf(AInterest: TAspect; AObserver: TObserver): Integer;
var
    i:Integer;
begin
    for i := 0 to self.Count - 1 do
    begin
        with Self.Items[i]^ do
            if (Interest = AInterest) and (Observer = AObserver) then
            begin
                Result := i;
                Exit;
            end;
    end;
    //---
    Result := -1;
end;

procedure TMapList.Notify(Ptr: Pointer; Action: TListNotification);
begin
    if Action = lnDeleted then
        Dispose(Ptr);
    //---
    inherited;
end;

procedure TConcreteSubject.Calc1;
begin
    FInterest := apCalcEvent1;
    FState := FState + 1;
    //---
    Self.Notify;
end;

procedure TConcreteSubject.Calc2;
begin
    FInterest := apCalcEvent2;
    FState := FState * 2;
    //---
    Self.Notify;    
end;

end.

原创粉丝点击