使用Delphi 2009 Enterprise,我在模型视图中为GoF访客模式创建了代码,并将代码分为两个单元:一个用于域模型类,一个用于访问者(因为我可能需要其他单元用于不同的访问者实现,一个单元中的所有东西?'Big ball of mud'提前!)。
unit VisitorUnit;
interface
uses
ConcreteElementUnit;
type
IVisitor = interface;
IElement = interface
procedure Accept(AVisitor :IVisitor);
end;
IVisitor = interface
procedure VisitTConcreteElement(AElement :TConcreteElement);
end;
TConcreteVisitor = class(TInterfacedObject, IVisitor)
public
procedure VisitTConcreteElement(AElement :TConcreteElement);
end;
implementation
procedure TConcreteVisitor.VisitTConcreteElement(AElement :TConcreteElement);
begin
{ provide implementation here }
end;
end.
和业务模型类的第二个单元
unit ConcreteElementUnit;
interface
uses
VisitorUnit;
type
TConcreteElement = class(TInterfacedObject, IElement)
public
procedure Accept(AVisitor :IVisitor); virtual;
end;
Class1 = class(TConcreteElement)
public
procedure Accept(AVisitor :IVisitor);
end;
implementation
{ Class1 }
procedure Class1.Accept(AVisitor: IVisitor);
begin
AVisitor.VisitTConcreteElement(Self);
end;
end.
看到问题?圆形单位参考。有优雅的解决方案吗?我想它需要带有基本接口/基类定义的“n + 1”附加单元以避免CR问题,并且需要像强硬派一样的技巧?
答案 0 :(得分:4)
我使用以下方案来实现灵活的访问者模式:
unit uVisitorTypes;
type
IVisited = interface
{ GUID }
procedure Accept(Visitor: IInterface);
end;
IVisitor = interface
{ GUID }
procedure Visit(Instance: IInterface);
end;
TVisitor = class(..., IVisitor)
procedure Visit(Instance: IInterface);
end;
procedure TVisitor.Visit(Instance: IInterface);
var
visited: IVisited;
begin
if Supports(Instance, IVisited, visited) then
visited.Accept(Self)
else
// raise exception or handle error elsewise
end;
unit uElement;
type
TElement = class(..., IVisited)
procedure Accept(Visitor: IInterface);
end;
// declare the visitor interface next to the class-to-be-visited declaration
IElementVisitor = interface
{ GUID }
procedure VisitElement(Instance: TElement);
end;
procedure TElement.Accept(Visitor: IInterface);
var
elementVisitor: IElementVisitor;
begin
if Supports(Visitor, IElementVisitor, elementVisitor) then
elementVisitor.VisitElement(Self)
else
// if override call inherited, handle error or simply ignore
end;
unit MyVisitorImpl;
uses
uVisitorTypes, uElement;
type
TMyVisitor = class(TVisitor, IElementVisitor)
procedure VisitElement(Instance: TElement);
end;
procedure TMyVisitor.VisitElement(Instance: TElement);
begin
// Do whatever you want with Instance
end;
uses
uElement, uMyElementVisitor;
var
visitor: TMyVisitor;
element: TElement;
begin
// get hands on some element
visitor := TMyVisitor.Create;
try
visitor.Visit(element);
finally
visitor.Free;
end;
end;
答案 1 :(得分:1)
为什么不定义IVisitor
IVisitor = interface
procedure VisitElement(AElement :IElement);
end;
然后TConcreteElement在它自己的单位:
unit ConcreteElementUnit;
interface
uses
VisitorUnit;
type
TConcreteElement = class(TInterfacedObject, IElement)
public
procedure Accept(AVisitor :IVisitor); virtual;
end;
Class1 = class(TConcreteElement)
public
procedure Accept(AVisitor :IVisitor);
end;
implementation
{ Class1 }
procedure Class1.Accept(AVisitor: IVisitor);
begin
AVisitor.VisitElement(Self);
end;
end.
这样你就不会混合类和接口引用(总是一个坏主意)
答案 2 :(得分:1)
以下实现在Visitor接口上使用泛型类型来解决Visitor
模式的循环引用问题:
Visitor.Intf.pas
:
unit Visitor.Intf;
interface
type
IVisitor<T> = interface
procedure Visit_Element(o: T);
end;
implementation
end.
Element.pas
:
unit Element;
interface
uses Visitor.Intf;
type
TElement = class
procedure Accept(const V: IVisitor<TElement>);
end;
implementation
procedure TElement.Accept(const V: IVisitor<TElement>);
begin
V.Visit_Element(Self);
end;
end.
Visitor.Concrete.pas
:
unit Visitor.Concrete;
interface
uses Element, Visitor.Intf;
type
TConcreteVisitor = class(TInterfacedObject, IVisitor<TElement>)
protected
procedure Visit_Element(o: TElement);
end;
implementation
procedure TConcreteVisitor.Visit_Element(o: TElement);
begin
// write implementation here
end;
end.
使用TElement和TConcreteVisitor类:
var E: TElement;
begin
E := TElement.Create;
E.Accept(TConcreteVisitor.Create as IVisitor<TElement>);
E.Free;
end;
答案 3 :(得分:0)
TConcreteElement的删除应该在VisitorUnit(或第三个单元)中
或更好
应将IVisitator更改为:
IVisitor = interface
procedure VisitTConcreteElement(AElement :IElement);
end;