Search code examples
delphiproject-organizationcircular-reference

How to avoid circular unit reference?


Imagine the following two classes of a chess game:

TChessBoard = class
private
  FBoard : array [1..8, 1..8] of TChessPiece;
...
end;

TChessPiece = class abstract
public
   procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>);
...
end;

I want the two classes to be defined in two separate units ChessBoard.pas and ChessPiece.pas.

How can I avoid the circular unit reference that I run into here (each unit is needed in the other unit's interface section)?


Solution

  • Change the unit that defines TChessPiece to look like the following:

    TYPE
      tBaseChessBoard = class;
    
      TChessPiece = class
        procedure GetMoveTargets (BoardPos : TPoint; Board : TBaseChessBoard; ...    
      ...
      end;    
    

    then modify the unit that defines TChessBoard to look like the following:

    USES
      unit_containing_tBaseChessboard;
    
    TYPE
      TChessBoard = class(tBaseChessBoard)
      private
        FBoard : array [1..8, 1..8] of TChessPiece;
      ...
      end;  
    

    This allows you to pass concrete instances to the chess piece without having to worry about a circular reference. Since the board uses the Tchesspieces in its private, it really doesn't have to exist prior to the Tchesspiece declaration, just as place holder. Any state variables which the tChessPiece must know about of course should be placed in the tBaseChessBoard, where they will be available to both.