1

Java に新しい機能を提供することを考えていますが、これまで設計上制限されてきた理由をお聞きしたいと思います。

public abstract class BodyPart {
    abstract public void followBodyPart(BodyPart part);
}

public class Head extends BodyPart{
     public void followBodyPart(Body body ) { //Why is this kind of implementation not allowed? 
     ...
     }
}
public class Body extends BodyPart{
     public void followBodyPart(Head head ) { //and this
     ...
     }
     public void followBodyPart(Forearm leftForearm ) { //and also this
     ...
     }
     ...
}
//Arm, Forearm, etc...

Head の followBodyPart(Body body) が BodyPart に followBody を実装していないのはなぜですか? もしそうなら、利点は明らかです。

まず、IDE は、オートコンプリート機能内で、Head が追従できない他の BodyParts オブジェクトの代わりに、FollowBody のパラメーターとして Body オブジェクトを提供できます。

第 2 に、Body の現在のバージョンは 1 つの関数と多数の instanceof で構成されており、これらは削除できる可能性があります。

最後に、このコードは Java ME デバイスに移植する必要があるため、ジェネリックはここで役立ちますが、問題を解決することはできません。

ここで発見したように、この質問は不適切なフォーラムですでに尋ねられています

答えに関しては、別の考え方をしてください。BodyPart を実装するものはすべてBodyPart を受け入れる必要があることを理解していますが、私が望むのは、Head がA BodyPart を 受け入れることができると言えるようにすることです。

ありがとう。

4

2 に答える 2

3

質問は、リンクしたフォーラムの投稿でも回答されています..

つまり; インターフェイスは、関数が実装するものを何でもBodyPart受け入れることができるように定義します。

サブクラスのみを受け入れ、他のサブクラスは受け入れないように関数Headを実装することにより。あなたはその契約に違反しています(実装するものを受け入れなくなったため)。BodyBodyPart

インターフェイスは通常、「外部」コードに提供するために使用され、提供されるインターフェイスの実装に関係なく、それを確認できるようにします。インターフェイスで定義された関数を確実に使用できます。

したがって、この外部コードが を取得した場合、引数として拡張するものすべてを受け入れることができるBodyPart関数があることがわかります。ただし、その外部コードは、取得したことを認識しない(または、instanceof チェックの後にキャストした後に取得できる) ため、インターフェイス関数が.followBodyPartBodyPartHeadBody


リクエストにより; BodyPartある種のプログラム API としてインターフェイスを提供するとします。その場合、それがどのタイプであるかを直接知る必要はありませんBodyPart。さて、私はそれらのうちの 2 つを持っているとしましょう。API の一部の関数を介して受信します。たとえば、署名: を使用しますpublic BodyPart getBody()。メソッドは、私が戻ってくる可能性があると述べています。Bodyしかし、それは何か他のものである可能性もあります (事実は、私にはわかりません!)。

BodyPartインターフェイスによると; followBodyPart最初のを呼び出してBodyPart、2 番目の を引数として渡すことができます。ただし、実際のBody実装ではこれは許可されません。そして、私がそれを知る方法はありません。

異なるクラスが異なるエントリを受け入れるようにしたい場合。関数を削除しBodyPartて、サブクラスに実装する必要があります。

これらのサブクラスを API から戻す。誰もが何と話しているか、何ができるかを知っています (例:public Body getBody()public Head getHead())。私は実際の実装クラスを持っているので、実際の実装には特定BodyPartの「従う」必要があるため、問題はありません。

他のオプションは、ジェネリックを使用することですが、質問では不可能であると述べられています。このような場合、次のように Interface を定義できます。

public interface Accepts<T extends BodyPart> {
    public void followBodyPart(T part);
}

API は、たとえば、実装された、BodyPartまたはインスタンスのいずれかを返すことができます。Accepts<Head>(編集: ここに書いたように、異なるジェネリック型で同じインターフェイスを複数回実装することはできないことに注意するのを忘れていました。そのため、ジェネリック インターフェイス メソッドには、実際に呼び出しを処理できるオブジェクトをカプセル化する実際の実装が必要になります。すべてがさらに混乱します)

ボーナス編集:もちろん、インターフェイスとして作成し、ジェネリックの問題を効果的に回避することもできますAcceptsHead:) AcceptsArm

BodyPartこの編集により、(おそらく非表示の) 実装クラスで特定の実装のみを指定しながら、(引数として使用して) 汎用インターフェイスを使用することが奇妙で悪い考えである理由が明確になることを願っています。

于 2012-06-29T10:55:26.137 に答える
0

まず第一に、私はあなたのクラスの関係を直感的に理解していません.それらは循環的であり、すでに悪い設計を示しています. その特定の構造がたまたま必要ないと言っているわけではありません。循環性を取り除くためのリファクタリングが、最終的にはより良い設計になる可能性があることを示唆しています。

あなたがやろうとしているように見えるのは、訪問者パターンを実装することです。ただし、基本クラスへの参照がある場合、特殊なメソッドの呼び出しをトリガーすることはできません。たとえば、コンパイラは意図したメソッドを選択できないため、ランタイムはインスタンスの切り替えを行う必要があります。あなたにとっては、せいぜいシンタックスシュガーにすぎません(scalaを調べてください、実際にそうしています)。

def bodyPart(part:BodyPart) =>
  part match {
     Head(h) => /* do something with head h */
     Foot(f) => /* do something with foot f */
     Toe(t) => /* do something with toe t */
  }

これを解決するもう 1 つの方法は、可能なすべての訪問者タイプを抽象的に noop することです。

public class BodyPart { // could have been abstract class
    public void followBodyPart(BodyPart part) { }
    public void followBodyPart(Head part) { }
    public void followBodyPart(Arm part) { }
    public void followBodyPart(Foot part) { }
    public void followBodyPart(Toe part) { }
}
public class Head { ... /* only implements Head, BodyPart, others error */ }
public class Arm { ... /* only implements Arm, Abdomen, etc */ }

これで、ビジター インボーカーはコンパイル時に正しいメソッドを静的に選択します。ただし、他のすべての入力タイプを適切に処理する方法を決定する必要があるため、実装ごとにさらに配管が必要です。しかし、それは良いことです。あいまいさがなくなります。

于 2012-06-29T11:09:25.503 に答える