delphiinterface

Use definining interface as generic parameter


I am trying to write a fluid interface, where I return the same interface used in the call to the caller to produce code such as below, but also allow the supporting class to be inheritable.

Result:=Imp.Add(5).Subtract(2).Value;

My stumbling block is that I can not use the defining interface in the parameter to the parent interface, as I receive this error:

Type IImp is not yet completely defined

type
  IAdd<T: IInterface> = interface
    function Add(Value: Integer): T;
  end;

  IAddSubtract<T: IInterface> = interface(IAdd<T>)
    function Subtract(Value: Integer): T;
  end;

//  IImp = interface;
  IImp = interface(IAddSubtract<IImp>)
    ['{B1CB6C84-B79C-4F01-8304-FD22CD7A1230}']
  end;

Solution

  • Your approach is completely wrong. If you want to have fluent interface, then you need to declare single interface with all required operations, not multiple interfaces. You also don't need generics to do that.

    In more complex scenarios you may need to switch to some other interface, similarly to what you have now when you want to get integer Value out at the end, but your example also doesn't need that.

    This is how fluent interface for your code should look like:

    type
      ICalculator = interface
        function Add(AValue: Integer): ICalculator;
        function Subtract(AValue: Integer): ICalculator;
        function GetValue: Integer;
        property Value: Integer read GetValue;
      end;
    
      TCalculator = class(TInterfacedObject, ICalculator)
      protected
        FValue: Integer;
      public
        function Add(AValue: Integer): ICalculator;
        function Subtract(AValue: Integer): ICalculator;
        function GetValue: Integer;
        property Value: Integer read GetValue;
      end;
    
    function TCalculator.GetValue: Integer;
    begin
      Result := FValue;
    end;
    
    function TCalculator.Add(AValue: Integer): ICalculator;
    begin
      FValue := FValue + AValue;
      Result := Self;
    end;
    
    function TCalculator.Subtract(AValue: Integer): ICalculator;
    begin
      FValue := FValue - AValue;
      Result := Self;
    end;
    

    And then you can use it like you wanted to:

    var
      Imp: ICalculator;
      Result: Integer;
    begin
      Imp := TCalculator.Create;
      Result := Imp.Add(5).Subtract(2).Value;
      ...
    

    Supporting class is inheritable, you can mark all the interface methods as virtual if you need to override the operation. If you mean something else by inheritable, then you need to be more precise in your requirements.