设计模式---Delphi 篇

来源:互联网 发布:虚拟机ubuntu大小调整 编辑:程序博客网 时间:2024/05/15 01:11

本文以“观察者设计模式介绍 “对象--关系映射 ORM ”

下面以Delphi  tiOPF 框架实现原理介绍:

Abstract the Visitor logic

Before we go any further, we must abstract the Visitor functionality away from the business objects so we never have to touch it again. We will do this in three stages. We will create abstract TVisitor and TVisited classes, then we will create an abstract business object, and business object list that will descend from TVisited. We will then re-factor our TPerson and TPeople classes to descend from the newly created abstract classes.

The class diagram of what we are aiming to build looks like this:

UML Visitors

The TVisitor has two methods, AcceptVisitor( ) and Execute( ). Both taking a single parameter of type TVisited. The TVisited has a single method called Iterate( ) which takes a single parameter of type TVisitor. TVisited.Iterate( ) calls the Execute method on the Visitor that is passed as a parameter against itself, and if it contains other object, against each one of the too. The function TVisitor.AcceptVisitor is necessary because we are building a generic framework. It will be possible to pass a visitor that is designed for handling TPeople a concrete instance of, say a TDog and we must have a mechanism for preventing this from causing an access violation. The TVisited descends from TPersistent because down the track, we will be implementing some functionality that requires RTTI. The interfaces of TVisitor and TVisited are shown below:

TVisitor = class(TObject)protected  function AcceptVisitor(pVisited : TVisited): boolean; virtual; abstract;public  procedure Execute(pVisited : TVisited ); virtual; abstract;end; // Both AcceptVisitor and Execute must be implemented in the concreate
TVisited = class(TPersistent)public  procedure Iterate(pVisitor: TVisitor); virtual;end;

Both TVisitor.AcceptVisitor and TVisitor.Execute must be implemented in the concrete class. The implementation of TVisited.Iterate, which contains a call to TVisitor.Execute, is shown below:

procedure TVisited.Iterate(pVisitor: TVisitor);begin  pVisitor.Execute( self ) ;end;

Step #5. Create an abstract business object, and abstract list object

We require two more abstract classes in our framework: An abstract business object, and a list container for the abstract business object. We shall call these TtiObject and TtiObjectList and the interface of these classes is shown below:

TtiObject = class(TVisited)privatepublic  constructor Create; virtual;end;

We will be adding significantly to TtiObject when we look in more detail at the business object framework, but for the time being, we just add a virtual constructor so we can uniformly override the constructor in descendent classes.

We want our list class, TtiObjectList to descend from TVisited so the generic visitor behavior can be implemented (actually, we want it to descend from TtiObject for reasons we will discuss later). Ideally, we would use interfaces to give our list class the iteration behavior, but much of this code base predates the popularity of interfaces, and I have not faced up to the task of re-factoring to take advantage the benefits they can offer.

To create a list class which descends from TVisited and TtiObject, we shall use object containment. The interface of TtiObjectList is shown below and the implementation is pretty much what you would expect.

TtiObjectList = class(TtiObject)private  FList: TObjectList;  function    GetList: TList;public  constructor Create; override;  destructor  Destroy; override;  property    List: TList read GetList;  procedure   Iterate( pVisitor: TVisitor); override;  procedure   Add(pData: TObject);end ;

The most important method in this class is the overridden procedure Iterate. In the abstract class TVisitor, iterate is implemented as the one line call pVisitor.Execute(self). In the TtiObjectList it is implemented like this:

procedure TtiObjectList.Iterate(pVisitor: TVisitor);var  i : integer ;begin  inherited Iterate(pVisitor);  for i := 0 to FList.Count - 1 do    (FList.Items[i] as TVisited).Iterate(pVisitor);end;

This is an important core concept. We now have two abstract business objects TtiObject and TtiObjectList. Both have an Iterate method that is passed an instance of a TVisitor as a parameter. In each case, the TVisitor’s Execute method is called with self as a parameter. This call is made via inherited at the top of the hierarchy. For the TtiObjectList class, each object in the owned list also has its Iterate method called with the visitor being passed as the parameter. This ensures that all objects in the hierarchy are touched by the Visitor.

Step #6. Create a Visitor manager

Now, back to the original problem we created for our selves in step #3. We don’t want to be spending all our time creating and destroying visitors. The solution is in the Visitor Manager.

The Visitor Manager performs two main tasks: It maintains a list of registered visitors (visitors are registered in the implementation section of the unit where they are declared.); and calls a group of visitors that are registered with a given command name against the data object it is passed.

To implement the Visitor manager, we will define three more classes: The TVisClassRef, TVisMapping and the TtiVisitorManager.

The TVisClassRef is a class reference type that will hold an instance TVisitor’s class. I find the help text description of class references a little confusing. The best way to understand them is with an example. Lets say we have our abstract Visitor class TVisitor, and a Visitor class reference type TVisClassRef. We also have a concrete Visitor called TSaveVisitor. The TVisClassRef type is declared like this:

TVisClassRef = class of TVisitor ;

This lets us write code like this:

procedure ExecuteVisitor(const pData: TVisited; const pVisClass: TVisClassRef);var  lVisitor: TVisitor;begin  lVisitor := pVisClass.Create;  try    pData.Iterate(lVisitor);  finally    lVisitor.Free;  end;end;

We pass two parameters to this procedure; pData which is an instance of TVisited (like our TPeople), a TVisClassRef, which could be TShowNameVisitor or TShowEMailAdrsVisitor. This procedure takes care of the tedious business of creating the visitor, calling iterate, then freeing the visitor when done.

The second class we create for our visitor manager is called TVisMapping. It is a simple data structure to hold two pieces of information: a TVisClassRef and a string called Command. The interface of TVisMapping is shown below:

TVisMapping = class(TObject)private  FCommand: string;  FVisitorClass: TVisClassRef;public  property VisitorClass: TVisClassRef read FVisitorClass write FVisitorClass;  property Command: string read FCommand write FCommand;end;

The final class we create is the TtiVisitorManager. When we register a Visitor with the Visitor Manager, an instance of TVisMapping is created and added to the list inside the TtiVisitorManager. The command and VisitorClass properties are set which allows us to execute a group of visitors identified by a string. The interface of the TtiVisitorManager is shown below:

TtiVisitorManager = class(TObject)private  FList: TObjectList;public  constructor Create;  destructor  Destroy; override;  procedure   RegisterVisitor(const pCommand: string; pVisitorClass: TVisClassRef);  procedure   Execute(const pCommand: string; pData: TVisited);end;

The key methods here are RegisterVisitor and Execute. RegisterVisitor is called in the implementation section of the unit where the Visitor is defined and is typically called like this:

initialization   gTIOPFManager.VisitorManager.RegisterVisitor('show', TShowNameVisitor);   gTIOPFManager.VisitorManager.RegisterVisitor('show', TShowEMailAdrsVisitor);

The implementation of RegisterVisitor is shown below (this code is much the same as the code found in a Delphi implementation of the Factory Pattern)

procedure TtiVisitorManager.RegisterVisitor(const pCommand: string;     const pVisitorClass: TVisClassRef);var  lData: TVisMapping;begin  lData := TVisMapping.Create;  lData.Command := pCommand;  lData.VisitorClass := pVisitorClass;  FList.Add(lData);end;

The other important method in the TtiVisitorManager is Execute. Execute takes two parameters, the command name which identifies the family of visitors to be executed, and the data object which is at the top of the tree to be iterated over. The implementation of Execute is shown below:

procedure TtiVisitorManager.Execute(const pCommand: string;     const pData: TVisited);var  i: integer;  lVisitor: TVisitor;begin  for i := 0 to FList.Count - 1 do    if SameText(pCommand, TVisMapping(FList.Items[i]).Command) then    begin      lVisitor := TVisMapping(FList.Items[i]).VisitorClass.Create;      try        pData.Iterate(lVisitor);      finally        lVisitor.Free;      end;    end;end;

To execute both the ShowName and ShowEMailAdrs visitors (the ones we registered above), one after the other we would make the following call to the Visitor manager.

gTIOPFManager.VisitorManager.Execute('show', FPeople);

Next, we will create some persistent Visitors that will let us make calls like

// To read from a text filegTIOPFManager.VisitorManager.Execute('read', FPeople);// To save to a text filegTIOPFManager.VisitorManager.Execute('save', FPeople);

but first we will use the tiListView and tiPerAwareControls to create a GUI to use while editing the list of TPeople.

参考更多:

1、http://tiopf.sourceforge.net/Doc/Concepts/2_TheVisitorFramework.shtml

2、微软.NET框架 关于“观察者”模式、委派/事件:

http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/builddistapp/ExploringtheObServerDesignPattern.mspx?mfr=true

《未完待续07.04.07》 即将补充完善,敬请关注!!!