質問
これはこれに対するフォローアップの質問のようなものです 質問.
次のような継承ツリーがあるとします。
Car -> Ford -> Mustang -> MustangGT
インターフェイスを定義するメリットはありますか? それぞれ これらのクラスのうち?例:
ICar -> IFord -> IMustang -> IMustangGT
おそらく他のクラス(たとえば、 Chevy
)実装したいと思います Icar
または IFord
そしておそらく IMustang
, 、しかしおそらくそうではありません IMustangGT
それはとても具体的だからです。この場合、インターフェースは不要でしょうか?
また、実装したいクラスはすべて、 IFord
から継承することで、その 1 つの継承を間違いなく使用したいと考えます。 Ford
コードが重複しないように。それが当然のことである場合、それを実装する利点は何ですか? IFord
?
解決
私の経験では、インターフェイスは、それぞれが同じメソッドに応答する必要がある複数のクラスがあり、それらのクラスの共通インターフェイスに対して作成される他のコードで交換可能に使用できる場合に最適です。インターフェースの最適な使用法は、プロトコルが重要ですが、基礎となるロジックがクラスごとに異なる場合です。ロジックを複製する場合は、代わりに抽象クラスまたは標準クラスの継承を検討してください。
そしてあなたの質問の最初の部分に答えて、私はあなたのクラスのそれぞれにインターフェースを作成することをお勧めします。これにより、クラス構造が不必要に乱雑になります。インターフェイスが必要な場合は、いつでも後で追加できます。これがお役に立てば幸いです!
アダム
他のヒント
また、 adamalexの応答に同意します。これは、応答するクラスによってインターフェースを共有する必要があるということです。特定のメソッド。
クラスが同様の機能を持っているが、先祖関係で互いに直接関連していない場合、インターフェースは、2つの機能を重複させずにその機能をクラスに追加する良い方法です。 (または、微妙な違いがあるだけの複数の実装があります。)
自動車のアナロジーを使用している間、具体的な例です。次のクラスがあるとしましょう:
Car -> Ford -> Escape -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid
車には wheels
があり、 Drive()
および Steer()
を使用できます。したがって、これらのメソッドは Car
クラスに存在する必要があります。 (おそらく Car
クラスは抽象クラスになります。)
先に進むと、 Ford
と Toyota
の区別が得られます(おそらく、車のエンブレムのタイプの違いとして実装されますが、これもおそらく抽象クラスです。 )
次に、最後に Escape
および Corolla
クラスがあります。これらのクラスは完全に自動車として実装されています。
では、ハイブリッド車両をどのように作成できますか?
FordsHybridDrive()
メソッドを追加する EscapeHybrid
である Escape
のサブクラスと、 Corollaのサブクラス
。メソッドは基本的に同じことを行っていますが、まだ異なるメソッドがあります。 うん。それ以上のことができるようだ。 ToyotasHybridDrive()
メソッドを使用した CorollaHybrid
である
ハイブリッドに HybridDrive()
メソッドがあるとしましょう。 (完全な世界で)2つの異なるタイプのハイブリッドを持ちたくないため、 HybridDrive()
メソッドを持つ IHybrid
インターフェースを作成できます。 。
したがって、 EscapeHybrid
または CorollaHybrid
クラスを作成する場合、行う必要があるのは < code> IHybrid インターフェース。
実際の例として、Javaを見てみましょう。オブジェクトと他のオブジェクトの比較を行うことができるクラスは、 Comparable
インターフェースを実装します。名前が示すように、インターフェースは comparable であるクラス用である必要があります。したがって、名前は&quot; Comparable&quot;です。
関心のある問題として、車の例は、インターフェースレッスンで使用されます。 Javaチュートリアル。
クラスの継承は、オブジェクトとは何か を表します(例:アイデンティティ)。これは問題ありませんが、ほとんどの場合、オブジェクトが であるということは、オブジェクトが行うほど重要ではありません。これがインターフェースの出番です。
インターフェースは、オブジェクトが何を行うか )、またはその動作を記述する必要があります。これにより、私はそれが振る舞いであり、その振る舞いを考えると意味のある一連の操作を意味します。
そのため、適切なインターフェイス名は通常、 IDriveable
、 IHasWheels
などの形式にする必要があります。この動作を説明する最善の方法は、よく知られている他のオブジェクトを参照することです。そのため、「これらの1つに似た行為」と言うことができます。 (例: IList
)が、その命名形式は少数派であると私見しています。
そのロジックを考えると、インターフェイスの継承が理にかなっているシナリオは、オブジェクトの継承が理にかなっているシナリオとは完全にまったく異なります。これらのシナリオは、多くの場合、互いに。
実際に必要なインターフェースを通して考えるのに役立つ希望:-)
あなたが参照する必要のあるもののためのインターフェースのみを作成すると言います。車について知る必要のある他のクラスや関数を持っているかもしれませんが、フォードについて知る必要があるものはどれくらいの頻度でありますか?
不要なものは作成しないでください。インターフェースが必要であることが判明した場合、戻ってそれらを構築するのはわずかな労力です。
また、教育的な面では、この階層のようなものを実際に構築していないことを願っています。これは、継承の使用目的ではありません。
そのレベルの機能が必要になった場合にのみ作成します。
コードのリファクタリングは常に進行中のプロセスです。
必要に応じてインターフェイスに抽出できるツールがあります。 例えば。 http://geekswithblogs.net/ JaySmith / archive / 2008/02/27 / refactor-visual-studio-extract-interface.aspx
インターフェイスを実装するクラスのメンバーとして、ICarとその他すべて(Make = Ford、Model = Mustangなど)を作成します。
Make == Whatever
をチェックするルートに行きたくない場合、ポリモーフィズムを使用するために、Fordクラスと、たとえばGMクラスを持ち、両方ともICarを実装します。あなたのスタイルに。
とにかく-私の意見では、これらは自動車の属性であり、逆ではありません-方法は一般的であるため、1つのインターフェイスが必要です:ブレーキ、スピードアップなど
フォードは他の車ができないことをすることができますか?そうは思いません。
最初の2つのレベルであるICarとIFordを作成し、その2番目のレベルのインターフェイスが必要になるまで2番目のレベルをそのままにします。
オブジェクトが問題ドメイン内で相互にどのように相互作用する必要があるかを慎重に検討し、特定の抽象概念の複数の実装が必要かどうかを検討してください。インターフェイスを使用して、他のオブジェクトが相互作用する概念に関する契約を提供します。
あなたの例では、フォードはおそらくメーカーであり、マスタングはメーカーフォードが使用するModelName値であることをお勧めします。したがって、次のようになります。
IVehichle-&gt; CarImpl、MotorbikeImpl-has-aメーカーhas-many ModelNames
この回答では、インターフェイスとクラスの違い、私はそれを説明しました:
- インターフェイスは、概念 の内容(&quot; &quot;の有効期間、コンパイル時)を公開し、 値 (MyInterface x = ...)
- クラスは、コンセプトが実行するものを実行します(実行時に実際に実行されます)、値またはオブジェクト(MyClass xまたはaMyClass.method())に使用されます
「Ford」変数(「値」の概念)にFordの異なるサブクラスを格納する必要がある場合は、IFordを作成します。それ以外の場合は、実際に必要になるまで気にしないでください。
これは1つの基準です。満たされない場合、IFordはおそらく役に立たないでしょう。
それが満たされる場合、前の回答で公開された他の基準が適用されます。FordがCarよりも豊富なAPIを持っている場合、IFordは多態性の目的に役立ちます。そうでなければ、ICarで十分です。
私のビューのインターフェイスは、クラスが特定の署名を実装するという要件を強制するためのツールです。私にとって、大文字のインターフェイスの冒頭にある私が個人の代名詞として名前を付けていると思います。 、特定のクラスの完全な公開署名をミラーリングするインターフェイスを作成しません。パブリックシグネチャ(動作)を分析し、メンバーを(例を使用して)IMove、IUseFuel、ICarryPassengers、ISteerable、IAccelerate、IDepreciateなどのような小さな論理グループ(小さいほど良い)に分けます。私のシステムの他のクラスが必要とするこれらのインターフェース
一般に、これ(およびOOの多くの質問)について考える最良の方法は、契約の概念について考えることです。
契約は、各当事者が満たさなければならない特定の義務を規定する、2つ(またはそれ以上)の当事者間の合意として定義されます。プログラムでは、これはクラスが提供するサービスであり、サービスを取得するためにクラスを提供する必要があるものです。インターフェイスは、インターフェイスを実装するクラスが満たす必要がある契約を示します。
それを念頭に置いて、あなたの質問は、使用している言語と何をしたいかによって多少異なります。
OOを何年も続けた後(たとえば、30年)、私は通常、特にJavaですべてのコントラクトのインターフェイスを記述します。モックオブジェクトを簡単に、ほぼ自明で作成できます。
インターフェースは汎用のパブリックAPIであることが意図されており、ユーザーはこのパブリックAPIの使用に制限されます。ユーザーが IMustangGT
のタイプ固有のメソッドを使用することを意図していない限り、インターフェイス階層を ICar
および IExpensiveCar
に制限することができます。
インターフェイスと抽象クラスからのみ継承します。
ほぼ同じクラスがいくつかあり、大部分のメソッドを実装する必要がある場合は、他のオブジェクトを購入するのと組み合わせて、 と Interface を使用します。
Mustang クラスが大きく異なる場合は、インターフェイス ICar だけでなく IMustang も作成します。
したがって、クラス Ford と Mustang は ICar から継承でき、Mustang と MustangGT は ICar から継承できます。 そして ムスタング。
Ford クラスを実装し、メソッドが Mustang と同じである場合は、Mustang から購入します。
class Ford{
public function Foo(){
...
Mustang mustang = new Mustang();
return mustang.Foo();
}