クラスはオブジェクトの属性とメソッドを定義するか、または青写真を与えることになっていると思いました。そして、インターフェースは、クライアントの契約として、メソッドのセットを提供することです。 (したがって、クラスはメソッドのセットも定義しているため、クラスもインターフェイスを提供します)。
ただし、Shapeというクラスがあるとします。次に、Moveableというインターフェースを追加します。ここで、velocity
などの属性をクラスに追加する必要がある場合があります。しかし、インターフェイスはメソッドに関するものだけです。それでは、インターフェイスのためにクラスに属性を追加することについてどう考えますか?
インターフェースは、他の誰かが作成した要件と考える必要があります。
要件を満たす実装は、自分のクラスに対して行う決定であると考える必要があります。
ほとんどのOO言語では、クラスの状態は実装の問題です。
そのため、適切に実装されたgetVelocityは、他の誰かの要件を満たす動作を提供するために必要なものです。あなたはあなたのために働くどんなテクニックでもその要件を実装するかもしれません。速度を整数として保存する場合、または最近サンプリングされた位置および時間イベントのキューからオンザフライで計算する場合、それはあなたの特権です。
もちろん、単純なメソッドを常に再実装する必要がないことは便利です。多くのOOP言語- Mixin でこれに対する解決策があります。言語に応じて、Mixinsは特にw/r/t状態に非常に異なる制約を持つことができます。
たとえば、Javaのデフォルトの実装には独自の状態はありませんが、インターフェースの他のメソッドにアクセスできます。したがって、getVelocityを実装する必要があるかもしれませんが、速度に関連する他のすべての計算は、デフォルトの実装を提供できます。
一方、Scalaの特性は非常にフル機能であるため、実装がクラスではなく特性のみでほぼ排他的に記述されているプログラムを確認するのが一般的です(具体的な実現は、オブジェクトとケースクラスによって提供されます)。
ミックスインのない言語では、委任は一般的なソリューションです。インターフェイスを採用するクラスには、動作を提供するプライベートメンバークラスも含まれます。元のクラスは、引き続きインターフェイスのメソッドを直接実装しますが、それらのメソッドの本体では、呼び出しをプライベートメンバーに委任するだけです。
インターフェースは、1つ以上のオブジェクトが示すことができる動作または役割を表すものと考えることができます。
他の多くのものと同様に、形状はmovableのようになります。移動可能なインターフェースを使用すると、それらが何であるかに関係なく、移動可能なもので作業を開始できます。
オブジェクトの内部実装を変更する必要があることに気付いても、特定の動作(インターフェイス)のseparateの概念には影響しません。ただし、動作を変更する必要があることに気付いた場合は、その動作を持つもの(オブジェクト)をすべて変更する必要があります。
速度がオブジェクトの内部実装の詳細にすぎない場合、インターフェースには影響しません(動作)。ただし、インターフェイスが表す動作が正しく定義されておらず、ベロシティが必要になっていることに気付いた場合は、それに応じてインターフェイスとインターフェイスを示す各オブジェクトを変更する必要があります。
うまくいけば、オブジェクトとインターフェースが2つのseparateの概念であることがわかります。それらはお互いに直交です。
velocity
のような属性は実装の詳細であるため、インターフェースには属していません。インターフェースは属性を追加するように指示しません。それを行うのは選択された実装です。 1つの実装はvelocity
属性を使用でき、他の実装はacceleration
を使用します。
したがって、インターフェースのために属性を追加するのではなく、選択した実装のために属性を追加します。そして通常は、インターフェース自体ではなく、インターフェースの実装方法を決定するのはプログラマーです。
すでに良い答えがありますが、私はこの部分に気づきました:
私はインターフェイスを単なる多重継承と考えるかもしれませんが、多重継承の問題を持ちたくないだけです[...]
「インターフェース」の概念が厳密にステートレスである言語で作業していると思います。私はしばしばそれらのいくぶん強引なものを見つけ、多重継承はあるものがそうであるようにするほど厄介ではないと思います。
それにもかかわらず、概念的なレベルでは、ステートレスインターフェイスは多くの場合、最も一般的であり、契約上および保守上の観点から最も理想的です。
車輪付きロボット
たとえば、ロボットがさまざまな場所(部屋、建物、国)に移動するように命令するロボットインターフェイスを使用して、大量のコードを記述したとします。
上記のインターフェースにロボットに車輪があるという考えのような属性が含まれている場合、それは私の要求をロボットに車輪があるという概念に結び付けます。私のリクエストは車輪のあるロボットでのみ機能します(他の輸送手段を使用して新しい場所に移動できる場合もありますが、少なくとも必要ですそれを行うための車輪を所有している)。
これは、ロボットの概念を少なくとも車輪を持っている人に一般化し、同様に、ロボットが何万行ものコードにまたがる可能性のある新しい場所に行くようにロボットに対して行ったすべての要求を一般化します。
次に、脚、ジェットパック、または分子輸送装置を備え、車輪をまったく必要としない新しいロボットが導入されたとしましょう。これは、すべての既存のロボットクラスと、このインターフェイスを介してそれらを使用するすべての既存のコードに問題を引き起こします。これで、ロボットのインターフェース/契約がステートレスである場合には必要のない多くのコードを書き直す必要があるというかなり厄介な立場にいます。
柔軟なインターフェースにより多様性が可能
一般に、変更に対して弾力性のあるコードを残すという観点から最も理想的なインターフェイスは、インターフェイスコントラクトに違反することなく、さまざまな具体的な実装のための多くの余地を残します。状態がインターフェイスに導入されると、多くの場合、豊富さが非常に急速に低下し、考えられるさまざまなソリューションを大幅に絞り込むことができます。
これは言語設計で絶対に強制されるべきですか?わかりません-私はこれを強制しない言語を好む人として偏っています。それでも、ステートレスインターフェイスにエラーが発生した場合は、通常ははるかに良いと思います。属性/状態は、関数よりもはるかに迅速に抽象化の一般性を低下させます。
しかし、インターフェイスはメソッドに関するものだけです。それでは、インターフェイスのためにクラスに属性を追加することについてどう思いますか?
これらは実装の問題です。 Movable
インターフェイスを実装するクラスの目標が新しい場所に移動することである場合、それを実装するためのあらゆる種類の方法があり、そのために必要なデータを表すこともできます。その柔軟性/自由を認めてみてください。
おそらくここでの懸念は、十数クラス後、同じ種類の定型文をたくさん複製してインターフェースを実装しているように感じることです。その場合、潜在的に抽象基本クラスに到達するか、構成(多くの面倒な作業を行うメンバーを格納する)を使用することができ、おそらく静的メソッド(ただし、それが非記述子ヘルパー機能のように見え始める場合は、ほとんどの総体) 、など.
ただし、そのような具体的な詳細については契約の一部として言及せず、ステートレスであることの結果として、インターフェースがいかに柔軟であるかを覚えておいてください。
クラスを作成せずに、そのためのインターフェースを作成します。それは完全に逆になっています。クラスの作成者は、クラスがサポートするインターフェースの定義を制御してはなりません。
インターフェイスは、クラス外の誰かによって定義されます。 「ここにオブジェクトが必要です。この機能セットがある限り、オブジェクトの種類はまったく気にしません」と誰かが言います。そして、誰かがインターフェースを定義します。
次に誰かがやって来て、「誰かがそのインターフェイスを持つオブジェクトを必要とするときに使用できるまったく新しいクラスを作成したいのですが、それ以外のものにはしたくない」と言い、インターフェイスと非常によく一致するクラスを作成します。他には何もない。
あるいは、「ここにずっと前に書いたクラスのこのインスタンスがあり、そのインターフェースを持つオブジェクトが必要な状況でそのインスタンスが欲しい」と彼らは言う。次に、インターフェイスをクラスに実装するために必要なすべてを追加します。おそらく、既に存在するがインターフェイスの要件に一致しない他のメソッドを呼び出すだけの新しいメソッドを作成するか、完全に新しいメソッドを追加します。
特に2番目のケースでは、インターフェースとそのメソッドはnotであり、クラスのインスタンスが通常どのように使用されることになっていますか。インターフェイスの実装は、インスタンスを既存の他のコードにプラグインできるようにするための特殊なコードです。
私見では
次のコードのように、内部クラス変数を公開しない方がよい傾向があります。
person.name="sampleName";
s = person.name;
消費者がクラスの内部コンポーネントに結合されないように、物事をカプセル化するのが好きです。
次のように、私はゲッターとセッターを使用して内部状態にアクセスしたり、内部状態を変更したりします。
person.setName("sampleName");
s = person.getName();
そうすることで、メンバーを非公開または保護します。
つまり、有効なインターフェースは次のようになります。
public interface Person {
public void setName(String value);
public String getName();
public void doSomething();
}
これは、クラスに何らかの影響を与える状態を意味します。おそらく、実装者はString型のname
クラスメンバーを持つでしょう。
最終行:
インターフェースは、ある方法で実装者に属性を持たせることができ、パブリックな方法で公開されるだけでなく、ゲッターとセッターを使用します。