《GOF设计模式》—观察者(OBSERVER)—Delphi源码示例:封装复杂的更新语义

    技术2022-05-19  28

    示例:封装复杂的更新语义 说明: 当目标和观察者间的依赖关系特别复杂时,可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器(ChangeManager)。它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。例如,如果一个操作涉及到对几个相互依赖的目标进行改动,就必须保证仅在所有的目标都已更改完毕后,才一次性地通知它们的观察者,而不是每个目标都通知观察者。 ChangeManager是一个中介者模式(Mediator)的实例。通常只有一个ChangeManager,并且它是全局可见的。这里单件模式(Singleton)可能有用。 代码:   unit uObserver5; interface uses     SysUtils, Classes,Dialogs; type     TSubject = class;     TChangeManager = class;     TObserver = class     public         destructor Destroy; override;         //---         procedure Update(ASubject: TSubject); virtual; abstract;     end;     TConcreteObserver = class(TObserver)     private         FState: Integer;     public         procedure Update(ASubject: TSubject); override;     end;     TSubject = class     private         FChman: TChangeManager;         FNotifyEnabled: Boolean;         procedure SetNotifyEnabled(const Value: Boolean);     public         constructor Create;         destructor Destroy; override;         //---         procedure Attach(AObserver: TObserver);         procedure Detach(AObserver: TObserver);         procedure Notify();         //---         property NotifyEnabled: Boolean read FNotifyEnabled write SetNotifyEnabled;     end;     TConcreteSubject = class(TSubject)     private         FState: Integer;         procedure SetState(const Value: Integer);     public         property State: Integer read FState write SetState;     end;          RMapInfo = record         case Integer of             0: (                 Item: Pointer;                 Items: TList;                 );             1: (                 Subject: TSubject;                 Observers: TList;                 );             2: (                 Observer: TObserver;                 Subjects: TList;                 );     end;     PMapInfo = ^RMapInfo;     TMapList = class(TList)     private         function GetItems(Index: Integer): PMapInfo;     protected         procedure Notify(Ptr: Pointer; Action: TListNotification); override;     public         procedure Add(AItem1, AItem2: Pointer);         function IndexOf(AItem: Pointer): Integer;         procedure Delete(AItem1, AItem2: Pointer);         //---         property Items[Index: Integer]: PMapInfo read GetItems;default;     end;     TChangeManager = class     private         FSubjects: TMapList;         FObservers: TMapList;     public         constructor Create;         destructor Destroy; override;         //---         class function Instance: TChangeManager;         //---         procedure Register1(ASubject: TSubject; AObserver: TObserver);         procedure UnRegister1(ASubject: TSubject; AObserver: TObserver);         procedure Notify; virtual; abstract;     end;     TSimpleChangeManager = class(TChangeManager)     public         procedure Notify; override;     end;     TDAGChangeManager = class(TChangeManager)     public         procedure Notify; override;     end; procedure Test; implementation var     FChangeManager: TChangeManager; procedure Test;     //---     procedure _Test1;     var         ASubject: TConcreteSubject;         AObserver: TObserver;     begin         ASubject := TConcreteSubject.Create;         AObserver := TConcreteObserver.Create;         try             ASubject.Attach(AObserver);             ASubject.State := 123;         finally             AObserver.Free;             ASubject.Free;         end;     end;     //---     procedure _Test2;     var         ASubject: TConcreteSubject;         AObserver1,AObserver2: TObserver;     begin         ASubject := TConcreteSubject.Create;         AObserver1 := TConcreteObserver.Create;         AObserver2 := TConcreteObserver.Create;         try             ASubject.Attach(AObserver1);             ASubject.Attach(AObserver2);             //---             ASubject.State := 123;         finally             AObserver1.Free;             AObserver2.Free;             ASubject.Free;         end;     end;     //---     procedure _Test3;     var         ASubject1,ASubject2: TConcreteSubject;         AObserver: TObserver;     begin         ASubject1 := TConcreteSubject.Create;         ASubject2 := TConcreteSubject.Create;         AObserver := TConcreteObserver.Create;         try             ASubject1.Attach(AObserver);             ASubject2.Attach(AObserver);             //---             ASubject1.State := 1;             ASubject2.State := 2;         finally             AObserver.Free;             ASubject1.Free;             ASubject2.Free;         end;     end;     //---     procedure _Test4;     var         ASubject1,ASubject2: TConcreteSubject;         AObserver1,AObserver2: TObserver;     begin         ASubject1 := TConcreteSubject.Create;         ASubject2 := TConcreteSubject.Create;         AObserver1 := TConcreteObserver.Create;         AObserver2 := TConcreteObserver.Create;         try             ASubject1.Attach(AObserver1);             ASubject1.Attach(AObserver2);             ASubject2.Attach(AObserver1);             ASubject2.Attach(AObserver2);             //---             ASubject1.State := 1;             ASubject2.State := 2;         finally             AObserver1.Free;             AObserver2.Free;             ASubject1.Free;             ASubject2.Free;         end;     end; begin     _Test4; end; constructor TSubject.Create; begin     inherited;     //---     FChman := TChangeManager.Instance; end; destructor TSubject.Destroy; begin     FChman.UnRegister1(self, nil);     //---     inherited; end; procedure TSubject.Attach(AObserver: TObserver); begin     FChman.Register1(Self, AObserver); end; procedure TSubject.Detach(AObserver: TObserver); begin     FChman.Unregister1(Self, AObserver); end; procedure TSubject.Notify(); begin     FChman.Notify; end; procedure TSubject.SetNotifyEnabled(const Value: Boolean); begin     FNotifyEnabled := Value;     if FNotifyEnabled then     begin         Self.Notify;         FNotifyEnabled := False;     end; end; procedure TConcreteObserver.Update(ASubject: TSubject); begin     FState := FState + TConcreteSubject(ASubject).State;     ShowMessage(IntToStr(FState)); end; constructor TChangeManager.Create; begin     if FChangeManager = nil then     begin         FChangeManager := Self;         FSubjects := TMapList.Create;         FObservers := TMapList.Create;     end     else         raise Exception.Create('Error'); end; destructor TChangeManager.Destroy; begin     FSubjects.Free;     FObservers.Free;     FChangeManager := nil;     //---     inherited; end; class function TChangeManager.Instance: TChangeManager; begin     if FChangeManager = nil then         FChangeManager := TSimpleChangeManager.Create;         //FChangeManager := TDAGChangeManager.Create;     //---     Result := FChangeManager; end; procedure TChangeManager.Register1(ASubject: TSubject;     AObserver: TObserver); begin     FSubjects.Add(ASubject, AObserver);     FObservers.Add(AObserver, ASubject); end; procedure TChangeManager.UnRegister1(ASubject: TSubject;     AObserver: TObserver); begin     FSubjects.Delete(ASubject, AObserver);     FObservers.Delete(AObserver, ASubject); end; procedure TSimpleChangeManager.Notify; var     i, j: Integer;     ASubject: TSubject;     AObserver: TObserver; begin     for i := 0 to FSubjects.Count - 1 do     begin         with FSubjects[i]^ do         begin             ASubject := Subject;             if ASubject.NotifyEnabled then             begin                 for j := 0 to Observers.Count - 1 do                 begin                     AObserver := Observers[j];                     AObserver.Update(ASubject);                 end;             end;         end;     end; end; procedure TMapList.Add(AItem1, AItem2: Pointer);     //---     procedure _Add; overload;     var         pData: PMapInfo;     begin         New(pData);         pData.Items := TList.Create;         //---         pData.Item := AItem1;         pData.Items.Add(AItem2);         //---         inherited Add(pData);     end;     //---     procedure _Add(AList: TList); overload;     var         AIndex: Integer;     begin         AIndex := AList.IndexOf(AItem2);         if AIndex < 0 then             AList.Add(AItem2);     end; var     AIndex: Integer; begin     AIndex := Self.IndexOf(AItem1);     if AIndex < 0 then         _Add     else         _Add(Self.Items[AIndex].Items); end; procedure TMapList.Delete(AItem1, AItem2: Pointer);     //---     procedure _DelItem(AList: TList);     var         AIndex: Integer;     begin         with AList do         begin             AIndex := IndexOf(AItem2);             if AIndex >= 0 then                 Delete(AIndex);         end;     end;     //---     procedure _Delete1;     var         i: Integer;     begin         for i := self.Count - 1 downto 0 do         begin             with Self.Items[i]^ do             begin                 _DelItem(Items);                 if Items.Count = 0 then                     inherited Delete(i);             end;         end;     end;     //---     procedure _Delete2;     var         AIndex: Integer;     begin         AIndex := self.IndexOf(AItem1);         if AIndex >= 0 then             inherited Delete(AIndex);     end;     //---     procedure _Delete3;     var         AIndex: Integer;     begin         AIndex := self.IndexOf(AItem1);         if AIndex >= 0 then         begin             with Self.Items[AIndex]^ do             begin                 _DelItem(Items);                 if Items.Count = 0 then                     inherited Delete(AIndex);             end;         end;     end; begin     if (AItem1 = nil) and (AItem2 = nil) then         Exit;     //---     if (AItem1 = nil) then         _Delete1     else if (AItem2 = nil) then         _Delete2     else         _Delete3; end; function TMapList.GetItems(Index: Integer): PMapInfo; begin     Result := inherited Items[Index]; end; function TMapList.IndexOf(AItem: Pointer): Integer; var     i: Integer; begin     for i := 0 to self.Count - 1 do     begin         if (Self.Items[i].Item = AItem) then         begin             Result := i;             Exit;         end;     end;     //---     Result := -1; end; procedure TMapList.Notify(Ptr: Pointer; Action: TListNotification); var     pData: PMapInfo; begin     if Action = lnDeleted then     begin         pData := Ptr;         pData.Items.Free;         Dispose(pData);     end;     //---     inherited; end; destructor TObserver.Destroy; begin     TChangeManager.Instance.UnRegister1(nil, self);     //---     inherited; end; procedure TDAGChangeManager.Notify; var     i: Integer;     ASubject: TSubject;     AObserver: TObserver; begin     for i := 0 to FObservers.Count - 1 do     begin         with FObservers[i]^ do         begin             AObserver := Observer;             if Subjects.Count > 0 then             begin                 ASubject := Subjects.Last;                 if ASubject.NotifyEnabled then                     AObserver.Update(ASubject);             end;         end;     end; end; procedure TConcreteSubject.SetState(const Value: Integer); begin     FState := Value;     Self.NotifyEnabled := True; end; initialization     FChangeManager := nil; finalization     if FChangeManager <> nil then         FChangeManager.Free; end.


    最新回复(0)