0

クラスAがあるとしましょう

class A
{
   Z source;
}

ここで、コンテキストは、「Z」が継承ツリー内の共通クラスを共有しない異なるクラス(たとえば、BとC)のインスタンスである可能性があることを示しています。

素朴なアプローチは、「Z」をインターフェイスクラスにし、クラスBとCに実装させることだと思います。

しかし、クラスAのインスタンスが使用されるたびに、「ソース」のタイプを知る必要があるため、何かがまだ私を納得させません。したがって、すべてが複数の「ifs」で終了します。将来的には、他のクラスがZを実装する可能性があり、このタイプの「ifs」をハードコーディングすると、間違いなく何かが壊れる可能性があります。

Zのインスタンスタイプごとに行われる作業が異なるため、Zに関数を追加しても問題を解決できないという問題が発生しています。

誰かが私にアドバイスをしてくれることを願っています。おそらくいくつかの有用なデザインパターンについてです。

ありがとう

編集:Aのインスタンスを取得するときにどこかで行う作業は、インターフェイスZの背後にあるクラスによってまったく異なります。これが問題です。「重要な仕事」を行うエンティティはZではなく、他の誰かです。 Zが誰であるかを知りたい。

Edit2:たぶん具体的な例が役立つでしょう:

class Picture
{
  Artist a;
}

interface Artist
{
}

class Human : Artist { }

class Robot : Artist {}

今どこかに、のインスタンスがありますPicture

Picture p = getPicture();
// Now is the moment that depending if the type of `p.a` different jobs are done
// it doesn't matter any data or logic inside Human or Robot
4

3 に答える 3

6

インターフェイスを使用するポイントは、これらのさまざまな実装を非表示にすることです。Aメソッドの意図または高レベルの目的を知っている必要があります。

の各実装によって実行される作業Zは異なる場合がありますが、その作業を呼び出すために使用されるメソッドシグネチャは同じである可能性があります。クラスAはメソッドを呼び出すだけでよくZ.foo()、Zの実装がであるかどうかに応じてBC異なるコードが実行されます。

実際の実装タイプを知る必要があるのは、2つの異なるタイプで完全に無関係な処理を実行する必要があり、それらがインターフェースを共有していない場合のみです。しかし、その場合、なぜそれらは同じクラスAによって処理されているのでしょうか。さて、 XMLスキーマから生成されたクラスである場合など、これが理にかなっている場合があり、それら変更することはできませんが、一般的には、設計を改善できることを示しています。BC

Pictureの例を追加したので更新しました。これは私の主張を裏付けるものだと思います。実装getPicture()は異なりますが、目的リターンの種類は同じです。どちらの場合も、アーティストは画像を返します。

発信者がロボットが作成した写真と人間が作成した写真を同じように扱いたい場合は、Artistインターフェースを使用します。彼らはただ写真が欲しいので、人間とロボットを区別する必要はありません!画像の作成方法の詳細はサブクラスに属し、呼び出し元にはこれらの詳細が表示されないようにする必要があります。発信者が画像の作成方法を正確に気にする場合、発信者はロボットや人間ではなく、画像をペイントする必要があります。デザインはまったく異なります。

サブクラスがまったく関係のないタスクを実行している場合(これはArtistの例が示しているものではありません!)、標準のJavaRunnableなどの非常にあいまいなインターフェイスを使用する可能性があります。この場合、呼び出し元はrun()メソッドが何を実行するかを実際には知りません。それは、であるものを実行する方法を知っているだけですRunnable

リンク

次の質問/記事は、いくつかの代替案を提案していinstanceofます。

また、次の記事では、あなたと同じように見える例を使用して、サンプルコードを示しています。

次の記事ではinstanceof、VisitorパターンやAcyclicVisitorなどの他のアプローチとのトレードオフについて説明しています。

于 2012-10-17T22:50:41.017 に答える
1

私が見ているのはOOPの原則の誤解であるため、もっと情報を投稿する必要があると思います。共通のインターフェースタイプを使用した場合、リスコフの置換原則により、ソースがどのタイプであるかは問題になりません。

于 2012-10-17T22:51:15.683 に答える
1

A、B、Cクラスをアルファ、ベータ、ガンマと呼びます。

おそらく、アルファは2つのバージョンに分割できます。1つはベータを使用し、もう1つはガンマを使用します。これにより、Alphaでのチェックが回避instanceofされます。これは、ご想像のとおり、実際にはコードの臭いです。

abstract class Alpha
{
    abstract void useSource();
}

class BetaAlpha extends Alpha
{
    Beta source;
    void useSource() { source.doSomeBetaThing(); }
}

class GammaAlpha extends Alpha
{
    Gamma source;
    void useSource() { source.doSomeGammaThing(); }
}

実際、これは非常に一般的です。FilesまたはSocketsのいずれかを使用できるStreamクラスのより具体的な例を考えてみましょう。また、この例では、FileとSocketは一般的なベースクラスから派生したものではありません。実際、それらは私たちの管理下にないかもしれないので、私たちはそれらを変更することはできません。

abstract class Stream
{
    abstract void open();
    abstract void close();
}

class FileStream extends Stream
{
    File file;
    void open()  { file.open();  }
    void close() { file.close(); }
}

class SocketStream extends Stream
{
    Socket socket;
    void open()  { socket.connect();    }
    void close() { socket.disconnect(); }
}
于 2012-10-17T22:52:14.690 に答える