Embarcadero Delphi
インターフェイス
サーチ…
備考
インタフェースは、明示的な実装の情報を提供することなく、メソッドやクラスの必要な情報と期待される出力を記述するために使用されます。
クラスはインターフェイスを実装でき、インターフェイスは互いに継承できます。クラスがインタフェースを実装している場合、インタフェースで公開されているすべての関数とプロシージャがクラスに存在することを意味します。
デルファイのインタフェースの特別な側面は、インタフェースのインスタンスが参照カウントに基づいたライフタイム管理を持つことです。クラスインスタンスの有効期間は手動で管理する必要があります。
これらのすべての側面を考慮すると、インタフェースを使用してさまざまな目標を達成できます。
- 操作のための複数の異なる実装を提供する(例えば、ファイル、データベースへの保存、または電子メールとしての送信、すべてをインターフェース "SaveData"として)
- 依存関係を減らし、デカップリングを改善し、コードをより保守性とテスト可能にする
- 生涯管理に悩まされることなく、複数のユニットでインスタンスを操作します(ただし、ここでも落とし穴が存在しますが、注意してください)。
インタフェースの定義と実装
インタフェースはクラスのように宣言されていますが、アクセス修飾子はありません( public
、 private
、...)。また、定義は許されていないので、変数と定数は使用できません。
インターフェイスには一意の識別子が常に必要です。これは、 Ctrl + Shift + Gを押して生成できます。
IRepository = interface
['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
インタフェースを実装するには、インタフェースの名前を基本クラスの後ろに追加する必要があります。また、クラスはTInterfacedObject
子孫である必要がありTInterfacedObject
(これは生涯管理にとって重要です)。
TDatabaseRepository = class(TInterfacedObject, IRepository)
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
クラスがインタフェースを実装するときは、インタフェースに宣言されているすべてのメソッドと関数を含む必要があります。そうでなければ、コンパイルされません。
注目すべき点の1つは、呼び出し側がインタフェースで動作する場合、アクセス修飾子は影響を与えないということです。例えば、インタフェースのすべての関数はstrict private
メンバとして実装できますが、インタフェースのインスタンスが使用されている場合は、別のクラスから引き続き呼び出すことができます。
複数のインタフェースの実装
クラスは、Delphiクラスでは不可能な複数のクラス( 複数継承 )から継承するのではなく、 複数のインタフェースを実装できます。これを実現するには、すべてのインタフェースの名前を基本クラスの後ろにコンマ区切りで追加する必要があります。
もちろん、実装クラスは、各インタフェースで宣言された関数も定義しなければなりません。
IInterface1 = interface
['{A2437023-7606-4551-8D5A-1709212254AF}']
procedure Method1();
function Method2(): Boolean;
end;
IInterface2 = interface
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
TImplementer = class(TInterfacedObject, IInterface1, IInterface2)
// IInterface1
procedure Method1();
function Method2(): Boolean;
// IInterface2
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
property Value: TObject read GetValue write SetValue;
end;
インタフェースの継承
インタフェースは、クラスとまったく同じように、互いに継承することができます。したがって、実装クラスは、インタフェースおよびすべての基本インタフェースの機能を実装する必要があります。しかし、この方法では、コンパイラーはimplentingクラスが基本インターフェースも実装していることを知らず、明示的にリストされているインターフェースのみを認識します。そのためas ISuperInterface
でTImplementer
を使用してas ISuperInterface
しません。これにより、すべての基本インターフェースも明示的に実装することになります(この場合、 TImplementer = class(TInterfacedObject, IDescendantInterface, ISuperInterface)
)。
ISuperInterface = interface
['{A2437023-7606-4551-8D5A-1709212254AF}']
procedure Method1();
function Method2(): Boolean;
end;
IDescendantInterface = interface(ISuperInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
TImplementer = class(TInterfacedObject, IDescendantInterface)
// ISuperInterface
procedure Method1();
function Method2(): Boolean;
// IDescendantInterface
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
property Value: TObject read GetValue write SetValue;
end;
インタフェースのプロパティ
インタフェース内の変数の宣言は不可能であるため、 property Value: TObject read FValue write FValue;
を定義する「高速」な方法( property Value: TObject read FValue write FValue;
)は使用できません。代わりに、Getterとsetter(それぞれ必要な場合のみ)をインタフェースで宣言する必要があります。
IInterface = interface(IInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
注目すべき点の1つは、実装クラスがプロパティを宣言する必要がないということです。コンパイラはこのコードを受け入れます:
TImplementer = class(TInterfacedObject, IInterface)
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
end;
しかし、1つの注意点は、このような方法では、クラスそのものではなく、インタフェースのインスタンスを通じてのみプロパティにアクセスできることです。また、プロパティをクラスに追加すると読みやすさが向上します。