⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xpobserver.pas

📁 类似于Java JUnit的单元测试
💻 PAS
📖 第 1 页 / 共 2 页
字号:
unit XPObserver;

{
 $Source: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/Common/XPObserver.pas,v $
 $Revision: 1.2 $
 $Date: 2004/05/03 15:07:15 $
 Last amended by $Author: pvspain $
 $State: Exp $

 XPObserver:

 Interfaces and base classes to handle the mutual reference problem, wherein
 two objects prevent each other from being destroyed. Each holds a
 reference to the other, consequently neither's reference counts can reduce to
 zero, and their destructors will never be called.

 There are two commonly observed instances of this problem. The first is
 Parent/Child, where the Parent is responsible for the lifetime of the Child.
 Parent/Child is a particular case of the more general second scenario,
 Observer/Subject, where there is no lifetime relationship between the parties.

 There are two parties in Observer/Subject, an Observer, and the party being
 observed, the Subject. The Observer must implement IXPObserver, and the Subject
 must implement IXPSubject. The base class TXPSubject implements both IXPSubject
 and IXPObserver, as this relationship is often chained - the Observer itself
 has Observers and so on. IXPObserver is easy to otherwise implement, as it
 has only one method, ReleaseSubject, which has a very simple implementation.

 The Observer obtains a reference to Subject and registers interest by calling
 Subject.AddObserver() and passing itself (as an IXPObserver or descendant
 interface) and the Subject (as an IXPSubject or descendant interface) as
 arguments. Optionally, the Observer can also pass some context-specific
 information as Context (defaults to nil), which will be returned unaltered
 in a later ReleaseSubject() callback on the Observer.

 When the Subject is being destroyed, it will call Observer.ReleaseSubject()
 on all registered Observers, passing itself (the Subject reference passed in
 via Subject.AddObserver()) and the Observer-supplied Context argument
 (defaults to nil) as the arguments.
 The Observers, in response, must release their reference to the Subject. The
 Subject then deletes the Observer from its list of Observers, releasing its
 reference to the Observer in the process.

 If an Observer wishes to initiate detachment from the Subject, it must call
 Subject.DeleteObserver(), passing itself (IXPObserver), and any Context passed
 earlier to Subject.AddObserver() as the argument. The Subject will respond by
 calling Observer.ReleaseSubject, wherein the Observer must release its
 reference to the Subject (Subject := nil).

 In the Parent/Child scenario, both parties must implement IXPFamily, to
 allow for hierarchical relationships. Use the base class TXPFamily for
 parents and children, passing the parent in the constructor and, typically, no
 further action is required. The interface provides accessor methods for the
 Parent and any Children. The Parent can also accept non-Child observers, as
 it derives from IXPSubject. 

 Copyright (c) 2002,2003 by The Excellent Programming Company Pty Ltd
 (Australia) (ABN 27 005 394 918).

 Contact Paul Spain via email: paul@xpro.com.au

 This unit is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This unit is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this unit; if not, the license can be viewed at:
 http://www.gnu.org/copyleft/lesser.html
 or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 Boston, MA  02111-1307  USA
 }

interface

uses
  XPInterfacedObject,   // TXPInterfacedObject
  SyncObjs,             // TCriticalSection
  Classes;              // IInterfaceList


type

/////////////////////////////////////////////////////////////////////////////
//   Interface declarations
/////////////////////////////////////////////////////////////////////////////

  IXPSubject = interface;

  IXPObserver = interface
    ['{DF1C9798-E422-42B0-8534-26CC28214DFB}']
    procedure ReleaseSubject(const Subject: IXPSubject;
      const Context: pointer);
  end;

  // IXPSubject inherits from IXPObserver to enable observer chains and
  // parent-child hierarchies, and provides a simple linkage for cascading
  // notifications.

  IXPSubject = interface(IXPObserver)
    ['{D7E3FD5D-0A70-4095-AF41-433E7E4A9C29}']
    function AddObserver(const Observer: IXPObserver;
      const Subject: IXPSubject; const Context: pointer = nil): boolean;
    function InsertObserver(const idx: integer; const Observer: IXPObserver;
      const Subject: IXPSubject; const Context: pointer = nil): boolean;
    function DeleteObserver(const Observer: IXPObserver;
      const Context: pointer = nil): boolean;
    procedure DeleteObservers;
    function ObserverCount: integer;
    function GetObserver(const idx: integer): IXPObserver;

    property Observers[const idx: integer]: IXPObserver read GetObserver;
    property Count: integer read ObserverCount;
  end;

  IXPFamily = interface(IXPSubject)
    ['{D624A50F-F6D3-426C-BB74-2503FE654546}']
    function GetParent: IXPFamily;
    procedure SetParent(const AParent: IXPFamily);

    property Parent: IXPFamily read GetParent write SetParent;
    property Children[const idx: integer]: IXPObserver read GetObserver;
  end;

  PInterface = ^IInterface;

  TXPReleaser = procedure(const LocalRef: PInterface) of object;

  // IXPSubjects:
  // Convenience interface to automate the management of subject/observer
  // relationships. Typically, an observer class (which contains references to
  // subjects) would contain a member instance of type IXPSubjects to
  // manage its subjects.

  IXPSubjects = interface(IXPObserver)
    ['{BA5B61FE-DE3D-47A4-83D6-5AD555E391D5}']
    // Add an interface to be managed, and an optional callback method
    // <Releaser> to be called when that interface must be released
    function AddSubject(const LocalRef: PInterface;
      const Releaser: TXPReleaser = nil): boolean;
    // Observer-initiated detachment from subject.
    function DeleteSubject(const LocalRef: PInterface): boolean;
    // Observer-initiated detachment from all subjects. Should be called
    // from container class's destructor.
    procedure Clear;
  end;

  
/////////////////////////////////////////////////////////////////////////////
//   Interface implementor declarations
/////////////////////////////////////////////////////////////////////////////

  PXPSubjectInfo = ^TXPSubjectInfo;

  TXPSubjectInfo = record
    AsPassed: PInterface;
    AsSubject: pointer;
    Releaser: TXPReleaser;
  end;

  TXPSubjects = class(TXPInterfacedObject, IXPObserver, IXPSubjects)
  private

    FSubjects: TList;
    // Initialised to false by default
    FDestroying: boolean;

    function FindInterface(const LocalRef: PInterface;
      out idx: integer): boolean;

  protected

    // IXPObserver implementation

    procedure ReleaseSubject(const ASubject: IXPSubject;
      const Context: pointer); virtual;

    // IXPSubjects implementation

    // We allow duplicate values, but not duplicate references, so pass by
    // reference to ensure there are no duplicate references. Said another way,
    // we can have more than one local reference (interface variable) to a
    // subject, but each local reference can only be registered once.
    function AddSubject(const LocalRef: PInterface;
      const Releaser: TXPReleaser = nil): boolean;
    // Observer initiated detachment from Subject. Pass by reference to
    // ensure we get the right reference
    function DeleteSubject(const LocalRef: PInterface): boolean;
    procedure Clear;

  public

    constructor Create(const ADelegator: IInterface = nil);
    destructor Destroy; override;
  end;

  TXPSubject = class(TXPSubjects, IXPSubject, IInterface)
  private

    FDeletingObservers: boolean;

    function FindObserver(const Observer: IXPObserver;
      const Context: pointer; out idx: integer): boolean;

  protected

    FSync: TCriticalSection;
    FObservers: TList;

    function SameContent(
      const ObserverA, ObserverB: IXPObserver): boolean; virtual;

    // IInterface partial reimplementation

    function _Release: Integer; stdcall;

    // IXPSubject implementation

    function AddObserver(const Observer: IXPObserver;
      const Subject: IXPSubject; const Context: pointer = nil): boolean;
    function InsertObserver(const idx: integer; const Observer: IXPObserver;
      const Subject: IXPSubject; const Context: pointer = nil): boolean;
    function DeleteObserver(const Observer: IXPObserver;
      const Context: pointer = nil): boolean;
    procedure DeleteObservers;
    function ObserverCount: integer;
    function GetObserver(const idx: integer): IXPObserver;


  public

    constructor Create(const ADelegator: IInterface = nil);
    destructor Destroy; override;
  end;

  TXPFamily = class(TXPSubject, IXPFamily)
  protected

    FParent: IXPFamily;

    // Reimplement method to cascade request to children.
    procedure ReleaseSubject(const Subject: IXPSubject;
      const Context: pointer); override;
    function GetParent: IXPFamily; virtual;
    procedure SetParent(const AParent: IXPFamily); virtual;

  public

    constructor Create(const AParent: IXPFamily = nil;
      const ADelegator: IInterface = nil);
  end;

implementation

uses
  SysUtils;

const CVSID: string = '$Header: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/Common/XPObserver.pas,v 1.2 2004/05/03 15:07:15 pvspain Exp $';

/////////////////////////////////////////////////////////////////////////////
//   TXPSubjects implementation
/////////////////////////////////////////////////////////////////////////////

constructor TXPSubjects.Create(const ADelegator: IInterface);
begin
  inherited Create(ADelegator);
  FSubjects := TList.Create;
end;

destructor TXPSubjects.Destroy;
begin
  // Set object-scope state so we can distinguish from user-initiated
  // detachment from subjects.
  FDestroying := true;
  Clear;
  FSubjects.Free;
  inherited Destroy;
end;

procedure TXPSubjects.Clear;
var
  idx: integer;
  SubjectInfo: PXPSubjectInfo;

begin
  // Disconnect from all subjects

  for idx :=  FSubjects.Count - 1 downto 0 do
  begin
    SubjectInfo := PXPSubjectInfo(FSubjects[idx]);
    // This call will consequentially invoke self.ReleaseSubject which will
    // delete the FSubjects item referenced by SubjectInfo. Hence, next
    // iteration we will be dealing with the new last item in FSubjects.
    // We could just deal with FSubjects[FSubjects.Count - 1] and avoid the
    // loop counter, idx, but there is potential for an infinite loop if the
    // subject doesn't call back into self.ReleaseSubject
    IXPSubject(SubjectInfo^.AsSubject).DeleteObserver(self,
      SubjectInfo^.AsPassed);
   end;

  // Taking a pessimistic view, clean up any subjects who haven't responded

  while FSubjects.Count > 0 do
  begin
    // Drop local reference to subject - don't call Releaser at this point
    // as we're probably in a minefield of dangling references
    PXPSubjectInfo(FSubjects[0])^.AsPassed^ := nil;
    // Finalise FSubjects entry
    System.Dispose(FSubjects[0]);
    FSubjects.Delete(0);
    // FSubjects.Count decrements and any remainder move up one slot in list
  end;

end;

function TXPSubjects.AddSubject(const LocalRef: PInterface;
  const Releaser: TXPReleaser): boolean;
var
  ASubject: IXPSubject;
  SubjectInfo: PXPSubjectInfo;
  idx: integer;

begin
  // We allow duplicate values but not duplicate references, so check for
  // matching reference
  Result := System.Assigned(LocalRef)
    and SysUtils.Supports(LocalRef^, IXPSubject, ASubject)
    and not FindInterface(LocalRef, idx);

  if Result then
  begin
    // Setup new item for FSubjects

    System.New(SubjectInfo);
    // Store ASubject as pointer to leave ref count unaffected
    SubjectInfo^.AsPassed := LocalRef;
    SubjectInfo^.AsSubject := pointer(ASubject);
    SubjectInfo^.Releaser := Releaser;

    // Add new item
    FSubjects.Add(SubjectInfo);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -