In Part 2 of this series we learned about a more flexible implementation of the Visitor Pattern than the traditional approach. But we can do better.
When we have a usual class inheritance within our shape or whatever classes, it might be desirable to handle groups of classes with a common ancestor all the same. For now we have to implement each interface individually. A slight change does the trick. We introduce a visitor interface for TAbstractShape, make TAbstractShape.Accept not abstract. Then we change the Accept implementations of the derived shapes to first check the visitor implementing the interface and call inherited otherwise.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
unit uShapeBase; interface uses Classes, SysUtils; type ENotSupported = class(Exception); type TAbstractShape = class public procedure Accept(Visitor: IInterface); virtual; end; IAbstractShapeVisitor = interface ['{7ED85A22-564A-4F23-AF32-37D29FD3C623}'] procedure VisitAbstractShape(Instance: TAbstractShape); end; { we avoid ref-counting here } TShapeVisitor = class(TInterfacedPersistent) public procedure Visit(Instance: TAbstractShape); end; type TShapeRectangle = class(TAbstractShape) private FX0: Double; FX1: Double; FY0: Double; FY1: Double; public procedure Accept(Visitor: IInterface); override; property X0: Double read FX0 write FX0; property X1: Double read FX1 write FX1; property Y0: Double read FY0 write FY0; property Y1: Double read FY1 write FY1; end; IShapeRectangleVisitor = interface ['{6F521E71-5DB8-4ECE-B453-60F9A3B8BE22}'] procedure VisitShapeRectangle(Instance: TShapeRectangle); end; type TShapeCircle = class(TAbstractShape) private FCenterX: Double; FCenterY: Double; FRadius: Double; public procedure Accept(Visitor: IInterface); override; property CenterX: Double read FCenterX write FCenterX; property CenterY: Double read FCenterY write FCenterY; property Radius: Double read FRadius write FRadius; end; IShapeCircleVisitor = interface ['{27D7DCD3-952A-4879-8667-E86023612BFB}'] procedure VisitShapeCircle(Instance: TShapeCircle); end; implementation procedure TShapeVisitor.Visit(Instance: TAbstractShape); begin Instance.Accept(Self); end; procedure TShapeCircle.Accept(Visitor: TShapeVisitor); var myVisitor: IShapeCircleVisitor; begin if Supports(Visitor, IShapeCircleVisitor, myVisitor) then myVisitor.VisitShapeCircle(Self) else inherited; end; procedure TShapeRectangle.Accept(Visitor: IInterface); var myVisitor: IShapeRectangleVisitor; begin if Supports(Visitor, IShapeRectangleVisitor, myVisitor) then myVisitor.VisitShapeRectangle(Self) else inherited; end; procedure TAbstractShape.Accept(Visitor: IInterface); var myVisitor: IAbstractShapeVisitor; begin if Supports(Visitor, IAbstractShapeVisitor, myVisitor) then myVisitor.VisitAbstractShape(Self) else raise ENotSupported.Create('Visitor doesn''t support ' + ClassName); end; end. |