2

私は、世界にあるものを表すいくつかのクラスを持つアプリを書いています。

World は、オブジェクトの配列 (ワールドに存在するオブジェクトのすべてのクラスが継承するクラスの配列 - それを Thing と呼びましょう) によって表されます。

class World {
  Thing[] myObjects;
}

class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}

ある時点で、特定の位置にあるオブジェクトが特定のタイプであるかどうかを知る必要があります。

私はいくつかの解決策を持っており、Java のオブジェクト モデル (CLOS) とは異なるオブジェクト モデルに慣れているため、Java のオブジェクト モデルに精通している人々とそれぞれのメリットについて話し合いたいと考えています。

解決策 1

World クラスでメソッド isAThingAAA(obj) および isAThingBBB(obj) を定義します。

これらのメソッドは obj.getClass () を呼び出し、返された型が AAA か BBB かをチェックします。

これで私が目にする問題は、「getClass」を使用して実装する必要があることです。またはそれを実装する別の方法はありますか?

解決策 2

Thing クラスでメソッド isAnAAA () および isAnBBB () を定義し、false を返すように実装します。true を返すように、それぞれのクラス (AAA.isAnAAA および BBB.isAnBBB) でそれらを再定義します。

最も抽象的なクラスはそのサブクラスの存在を知っているため、これは奇妙なことです。

他の提案?

前もって感謝します。

4

5 に答える 5

4

AAAクラス Thing に抽象メソッドを記述し、それをのインスタンスとのインスタンスに再定義させるのはどうでしょうかBBB。メソッドを書きたいようですが、そのisAnXXX理由を説明できるかもしれません。

instance ofオペレーターとメソッドを使用するとisAnXXX、ポリモーフィズムが発生しない可能性があります。そして、それは良いことではありません。あなたはポリモーフィズムを望んでいます、あなたはそれを必要としています...ゴラム、ゴラム。CCCまた、明日クラスをに追加したいと考えてくださいWorldオープン/クローズWorldの原則のように、クラスが変更されないことを設計で保証する必要があります。

要約すると、これを行うことができます:

クラスのもので:

public abstract class Thing{
   abstract void doSomething();
}

次に、子クラスでオーバーライドします

public class AAA extends Thing{

@override
public void doSomething(){ /*do something in AAAs way*/}

}

public class BBB extends Thing{

@override
public void doSomething(){ /*do something in BBBs way*/}

}

次に、あなたを埋めてThing[]これを行うことができます

   for (Thing t:myOBjects){
       t.doSomething()

   }

の各インスタンスは、そのタイプを尋ねることなく、Thing方法を知っています。doSomething

于 2011-07-23T16:51:29.957 に答える
3

別のオプションは、これらのメソッドをまったく持たないことです。これらの方法は、何をすべきかを決定するためによく使用されます。何かのようなもの

if(thing is a AAA) {
   ((AAA) thing).method();
} else if (thing is a BBB) {
   ((BBB) thing).method();
}

代わりに、アクションが必要なときに何をすべきかを各モノが知っている方がよいでしょう。あなたが電話しなければならないのは、

thing.method(); // each type know what to do.
于 2011-07-23T16:53:02.660 に答える
1

instanceof演算子を使用する必要があります

AAA aaa = new AAA();
if(aaa instanceof AAA) {
    //do something different
}

編集:また、トムの答えはあなたの問題を解決するのに十分であり、それは良い習慣です。

于 2011-07-23T16:55:17.673 に答える
0

最も簡単な方法は、Javaの組み込みのinstanceof演算子を使用することです。例えば:

public boolean isAAA(Thing myThing)
{
  return myThing instanceof AAA;
}

ただし、多くの人は、instanceofの使用はクラス設計の不備の兆候であり、さまざまなサブクラスでさまざまな動作を実現する適切な方法はポリモーフィズムを使用することであると言うでしょう。ここでの問題は、Javaでは、派生型に基づいてクラスが異なることを行うのは非常に簡単ですが、ハンドルを持つ派生型に応じて、他のオブジェクトがThingを異なる方法で処理するようにするのは少し難しいことです。の上。これはダブルディスパッチの問題です。

Thingオブジェクトを処理するときに、次のように、Thingがどのサブクラスであるかに応じて、Thingで異なることを行う他のメソッドに問題をディスパッチできると理想的です。

public void handleThing(Thing myThing)
{
  reactToThing(myThing);
}

public void reactToThing(AAA myThing)
{
  // Do stuff specific for AAA
}

public void reactToThing(BBB myThing)
{
  // Do stuff specific for BBB
}


public void reactToThing(Thing myThing)
{
  // Do stuff for generic Thing
}

ただし、単一のディスパッチのみをサポートするJavaでは、handleThing()のmyThingの実際のタイプに関係なく、reactToThing(Thing)が常に呼び出され、独自の動作が得られることはありません。

この問題を回避するために必要なことは、VisitorPatternを使用することです。これには、Thingクラスとそのすべての子に追加のコードを配置して、reactToThing()メソッドに追加のコンテキストを与えることが含まれます。したがって、上記のメソッドがすべて、Visitorというクラスにあるとしましょう。最初に問題をThingオブジェクト自体に渡すことで、上記を書き直して機能させることができます。次に、ポリモーフィズムにより、オブジェクトが訪問者に返す適切なコンテキスト(Thing、AAA、またはBBB)が得られます。

したがって、上記の例を次のように書き直すことができます。

public class Visitor
{
  // ...

  public void handleThing(Thing myThing)
  {
    myThing.accept(this);
  }

  public void reactToThing(AAA myThing)
  {
    // Do stuff specific for AAA
  }

  public void reactToThing(BBB myThing)
  {
    // Do stuff specific for BBB
  }


  public void reactToThing(Thing myThing)
  {
    // Do stuff for generic Thing
  }
}


public class Thing
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class AAA
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}


public class BBB
{
  // ...

  public void accept(Visitor visitor)
  {
    visitor.reactToThing(this);
  }
}

では、なぜ両方のサブクラスで同じaccept()メソッドを書き直す必要があったのでしょうか。それ以外の場合、visitor.reactToThing(this)メソッドを呼び出すときに、オブジェクトは引き続きThingコンテキストにあるため、以前と同じ問題が発生します。3つの場所すべてで再実装することにより、派生クラスは親の実装をオーバーライドし、Visitorで正しいメソッドが呼び出されます。

作業している派生クラスだけを知りたい場合は、多くの作業のように見えますが、その見返りは拡張性と保守です。これで、if(somethingElseの何かのインスタンス)をあちこちに追加する必要はありません。具体的には、たとえば、Visitorを拡張するなど、将来的に何かを変更する必要があると判断した場合に、維持する必要のある重複コードが少なくなります。

それがあなたの質問に対処することを願っています。

于 2011-07-23T17:16:09.213 に答える
0

instanceof または isInstance を使用できますhttp://download.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29

class World {
    Thing[] myObjects;
}
class Thing {}
class AAA extends Thing {}
class BBB extends Thing {}
public class Main {
    public static void main(String[] args) {
        Thing[] things={new AAA(),new BBB()};
        for(Thing thing:things)
            if(thing instanceof AAA)
                System.out.println("i am an AAA");
            else if(thing instanceof BBB)
                    System.out.println("i am a BBB");
        }
}
于 2011-07-23T17:06:18.360 に答える