示例:健壮的迭代器 说明: 一个健壮的迭代器(robustiterator)保证插入和删除操作不会干扰遍历,且不需拷贝该聚合。有许多方法来实现健壮的迭代器。其中大多数需要向这个聚合注册该迭代器。当插入或删除元素时,该聚合要么调整迭代器的内部状态,要么在内部的维护额外的信息以保证正确的遍历。 代码: unit uObserver; interface uses classes; type TObserver = class; TSubjectData = class protected FObs: TList; FNotifyEnabled: Boolean; procedure NotifyObserver; procedure SetEnableNotify(AEnable: Boolean); public constructor Create; destructor Destroy; override; //--- procedure Attach(AObserver: TObserver); procedure Detach(AObserver: TObserver); procedure UpdateObserver; //--- property NotifyEnabled: Boolean read FNotifyEnabled write SetEnableNotify; end; TObserver = class protected FSubjectData: TSubjectData; public constructor Create(ASubjectData: TSubjectData); destructor Destroy; override; //--- procedure Update; virtual; end; implementation constructor TSubjectData.Create; begin FObs := TList.Create; FNotifyEnabled := true; end; destructor TSubjectData.Destroy; begin FObs.Free; end; procedure TSubjectData.Attach(AObserver: TObserver); begin FObs.Add(AObserver); end; procedure TSubjectData.Detach(AObserver: TObserver); begin FObs.Remove(AObserver); end; procedure TSubjectData.NotifyObserver; var i: integer; begin for i := 0 to FObs.Count - 1 do TObserver(FObs[i]).Update; end; procedure TSubjectData.UpdateObserver; begin NotifyObserver; end; procedure TSubjectData.SetEnableNotify(AEnable: Boolean); begin FNotifyEnabled := AEnable; if FNotifyEnabled then NotifyObserver; end; constructor TObserver.Create(ASubjectData: TSubjectData); begin FSubjectData := ASubjectData; FSubjectData.Attach(Self); end; destructor TObserver.Destroy; begin FSubjectData.Detach(Self); //--- inherited; end; procedure TObserver.Update; begin end; end. unit uRobustIterator; interface uses SysUtils, Classes, Dialogs, uObserver; type TAggregateState = (tsAdd, tsInsert, tsDelete); TConcreteAggregate = class; TIterator = class(TObserver) public procedure First(); virtual; abstract; procedure Next(); virtual; abstract; function IsDone(): Boolean; virtual; abstract; function CurrentItem(): string; virtual; abstract; end; TConcreteIterator = class(TIterator) private FAggregate: TConcreteAggregate; FCurrent: integer; public constructor Create(const Aggregate: TConcreteAggregate); //--- procedure First(); override; procedure Next(); override; function IsDone(): Boolean; override; function CurrentItem: string; override; //--- procedure Update; override; end; TAggregate = class(TSubjectData) public function CreateIterator(): TIterator; virtual; abstract; end; TConcreteAggregate = class(TAggregate) private FList: TStringList; FState: TAggregateState; FIndex: Integer; function GetCount: integer; function GetItems(Index: integer): string; public constructor Create; destructor Destroy; override; //--- function CreateIterator: TIterator; override; //--- procedure Add(s: string); procedure Insert(Index: Integer; s: string); procedure Delete(Index: Integer); //--- property Count: integer read GetCount; property Items[Index: integer]: string read GetItems; default; end; procedure Test; implementation procedure Test; var Aggregate: TConcreteAggregate; AIterator: TIterator; i:Integer; begin Aggregate := TConcreteAggregate.Create; AIterator := Aggregate.CreateIterator; try with Aggregate do begin Add('111'); Add('222'); Add('333'); end; //--- i := 0; with AIterator do begin First; while not IsDone do begin Inc(i); if i = 2 then Aggregate.Delete(1); //--- ShowMessage(CurrentItem); Next; end; end; finally AIterator.Free; Aggregate.Free; end; end; constructor TConcreteIterator.Create(const Aggregate: TConcreteAggregate); begin inherited Create(Aggregate); //--- FAggregate := Aggregate; FCurrent := 0; end; procedure TConcreteIterator.First; begin FCurrent := 0; end; procedure TConcreteIterator.Next; begin FCurrent := FCurrent + 1; end; function TConcreteIterator.IsDone: Boolean; begin Result := FCurrent >= FAggregate.Count; end; function TConcreteIterator.CurrentItem: string; begin if self.IsDone then raise Exception.Create('Iterator Out Of Bounds'); //--- Result := FAggregate.Items[FCurrent]; end; constructor TConcreteAggregate.Create; begin inherited; //--- FList := TStringList.Create; end; destructor TConcreteAggregate.Destroy; begin FList.Free; //--- inherited; end; function TConcreteAggregate.CreateIterator: TIterator; begin Result := TConcreteIterator.Create(self); end; function TConcreteAggregate.GetCount: integer; begin Result := FList.Count; end; function TConcreteAggregate.GetItems(Index: integer): string; begin Result := FList[Index]; end; procedure TConcreteAggregate.Add(s: string); begin FList.Add(s); //--- FState := tsAdd; FIndex := FList.Count - 1; //--- self.NotifyEnabled := True; end; procedure TConcreteIterator.Update; begin with FAggregate do begin case FState of tsDelete: begin if FIndex < FCurrent then FCurrent := FCurrent - 1; end; end; end; end; procedure TConcreteAggregate.Delete(Index: Integer); begin FList.Delete(Index); //--- FState := tsDelete; FIndex := Index; //--- self.NotifyEnabled := True; end; procedure TConcreteAggregate.Insert(Index: Integer; s: string); begin FList.Insert(Index, s); //--- FState := tsInsert; FIndex := Index; //--- self.NotifyEnabled := True; end; end.
