Diagnoseproblem bei der Überprüfung von Voraussetzungen



  • Situation ist die folgende:

    class Node
    {
    public:
        virtual bool canConnectTo (Node* anotherNode);
        void connectTo (Node* anotherNode);
    };
    

    Was da passieren soll, ist leicht ersichtlich: canConnectTo() überprüft die Gültigkeit der einzugehenden Verbindung (in meinem Fall habe ich beispielsweise einen gerichteten azyklischen Graphen und möchte folgerichtig Zyklen verhindern), und connectTo() ruft zunächst canConnectTo() auf und wirft im Bedarfsfall eine Exception.

    Das Problem ist nun, daß diese Exception nicht aussagekräftig ist, denn connectTo() weiß nicht, warum die Verbindung nicht möglich ist.

    Ein naheliegender Ansatz zur Lösung wäre, connectTo() gleichfalls virtuell zu machen und alle Überprüfungen, die in canConnectTo durchgeführt werden, erneut durchzuführen; das finde ich aber aufgrund der Codeduplizierung nicht schön.

    Zurzeit löse ich das Problem so:

    struct Node
    {
    protected:
        virtual bool canConnectTo (Node* anotherNode, bool throwIfNot);
    public:
        bool canConnectTo (Node* anotherNode);
        void connectTo (Node* anotherNode);
    };
    bool canConnectTo (Node* anotherNode)
    {
        return canConnectTo (anotherNode, false);
    }
    void connectTo (Node* anotherNode)
    {
        canConnectTo (anotherNode, true);
       ...
    }
    

    Dann kann direkt bei der Überprüfung in canConnectTo() eine aussagekräftige Exception geworfen werden. Das ist eine sehr pragmatische Lösung, an der mir lediglich die Funktion canConnectTo() mißfällt, die damit zu einem etwas häßlichen Hybriden geworden ist und zweierlei Zwecke erfüllt.

    Hat jemand einen schöneren Vorschlag?



  • Problem hat sich gelöst. Mir fiel auf, daß ich doch etwas mehr Flexibilität brauche als einfach eine Exception; ich möchte auch etwa in einem interaktiven Graphen-Designer ein dem Grund entsprechendes Cursor-Symbol anzeigen können. Die Lösung sieht so aus (und läßt sich nicht direkt in C++ übersetzen, daher in der Originalsprache Delphi):

    type
      TConnectionFailure = class
      public
        class function Reason: String; virtual; abstract;
        class function CursorOverlay: TCursor; virtual; abstract;
      end;
    
      TCycleConnectionFailure = class (TConnectionFailure)
        ...
      end;
    
      TNode = class
      public
        function CanConnectTo (ANode: TNode; out Reason: TConnectionFailureClass): Boolean; overload; virtual;
        function CanConnectTo (ANode: TNode): Boolean; overload;
        function ConnectTo (ANode: TNode);
      end;
    
    function TNode.CanConnectTo (ANode: TNode; out Reason: TConnectionFailureClass): Boolean;
    begin
      Result := not ConnectionIntroducesCycle (ANode);
      if not Result then
        Reason := TCycleConnectionFailure;
    end;
    
    function TNode.CanConnectTo (ANode: TNode): Boolean;
    var
      Reason: TConnectionFailureClass;
    begin
      Result := CanConnectTo (ANode, Reason);
    end;
    
    function TNode.ConnectTo (ANode: TNode);
    var
      Reason: TConnectionFailureClass;
    begin
      if not CanConnectTo (ANode, Reason) then
        raise Exception.CreateFmt ('Cannot connect %s to %s: %s',
          [Self.ToString, ANode.ToString, Reason.Reason]);
      ...
    end;
    

Anmelden zum Antworten