2017-04-26 1 views
2

をサポートしている必要があり、私はそうのようなセットアップを持っている:パラメータインタフェースY

IBuilder = interface(IInvokable) 
end; 

IBuilder<T: IBuilder; TOut : TWinControl> = interface(IInvokable) 
end; 

TBuilder<T: IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>) 
end; 

TBuilder = class(TBuilder<TBuilder, TWinControl>) 
end; 

この種の構造は、私はそうのような砂糖の構文を構築することができます:

TBuilder<T : IBuilder; TOut : TWinControl> = class(TInterfacedObject, IBuilder, IBuilder<T, TOut>) 

    function Output : TOut; 
    function Name(aName : string) : T; 
    function Left(aLeft : Integer) : T; 
    function Top(aTop : Integer) : T; 

end; 

// ... later 

TBuilder.Create().Left(10).Top(5).Name('ABC'); // Nice one liner 

問題があることです私は

E2514 The type parameter TBuilder must support interface 'IBuilder'. 

これはおそらく、入力された制約T: IBuilder preseによるものであると言って、コンパイル・エラーが発生しますTBuilderはIBuilderをサポートしていますが(祖先)

誰でもこの問題を回避する方法を教えてください。

けれども、私はこれを行うことができないTBuilder = class(TBuilder<IBuilder, TObject>)

答えて

2

を使用することはできません。あなたは、本質的にこれを実行しようとしている:生成

IBar = interface(IInterface) end; 

    TFoo<T : IBar> = class(TObject, IBar) end; 

    TBar = TFoo<TBar>; 

エラー

E2086タイプ「TBar」まだ完全にあなたがこれを書くことができ、インタフェース依存せずに

定義されていません。

TBar = class(TFoo<TBar>) end; 

アリアだけでなく、それを真の子孫にするとして。これは通常型を解決することができますが、インタフェースの依存関係によってコンパイラは質問をすることになります。TBarIBarに対応していますか?

あなたが考えてみれば、これはのように動作します:あなたは無限再帰の問題を解決するために、コンパイラを求めている

TBar = TFoo<TBar> {TBar support IBar?} 
      | 
      TBar = TFoo<TBar>... {ok, TBar support IBar?} 
          | 
         TBar = TFoo<TBar> {ok, TBar support IBar?} 
             | 
             {...turtles all the way down} 

。これはできません。

+0

おそらくベースタイプ、デルファイの「カスタム」パターンなどを使用して回避策があるかどうか知っていますか? – Ludo

+0

@ルドーは何も気にしませんが、個人的には、私がこのアプローチを完全に避けるのは、それが本当にあなたにマイナーな砂糖よりも大きなメリットをもたらさない限りです。具体的なメリットがある場合は、実装する方が良いでしょう。あなたがこれをどのように働かせたいかについてもっと知らずに言うのは難しいです。 –

0

これは、メソッドの戻り値の型を変更し、再帰型のパラメーターを除外することで解決できます。

interface 
type 
    //IBuilder = interface(IInvokable) 
    //end; //I don't think you need this 

    IBuilder<TOut : TWinControl> = interface(IInvokable) 
    function Output : TOut; 
    function Name(const aName : string) : IBuilder<TOut>; 
    function Left(aLeft : Integer) : IBuilder<TOut>; 
    function Top(aTop : Integer) : IBuilder<TOut>; 
    end; 

TFactory<TOut: TWinControl> = record 
    class function New: IBuilder<TOut>; static; 
end; 

implementation 
type  
    //Put the actual class in the implementation 
    TBuilder<TOut : TWinControl> = class(TInterfacedObject, IBuilder<TOut>) 
    //see interface 
    end; 

あなたは、通常そのようにこれを使用する:

var 
    MyButton: IBuilder<TButton>; 
begin 
    MyButton:= TFactory<TButton>.New.Left(10).Top(5).Name('ABC'); 

インターフェイスを使用している場合、あなたは、排他的インタフェースと対話いつも、クラスで動作することはありません。実装でクラス定義を移動することにより、これを実行します。これを補うには、インターフェイスにファクトリメソッドを追加します。

この場合、一般的なスタンドアロン方式を使用することはできないため、レコードでなければなりません。

class function TFactory<TOut>.New: IBuilder<TOut>; 
begin 
    Result:= TBuilder<TOut>.Create; 
end; 
+0

あなたの解決策の問題は、例えば "TComponentBuilder <| ---- TFormBuilder"のような "ビルダー"を継承できない場合です。私の場合は、ビルド(TComponentBuilder、TWinControlBuilder、TFormBuilder)でビルドしたいクラス(TComponent、TWinControl、TFormなど)をミラーリングしました。すべてのBuilderクラスにはメソッドが豊富に用意されています(TComponentBuilderにはName(aName:文字列)、TWinControlBuilderにはAlign(aAlign)、TFormBuilderにはFormStyle(aFormStyle)関数があります... – Ludo