开发者

Java object-model and type identified methods

I'm writing an app that has a few classes which represent things that are in a World.

The World is represented by an array of objects (of the class from which all classes of objects that exist in the world inherit - lets call it Thing).

class World {
  Thing[] myObjects;
}

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

At some point I need to know if an object at a given position is of a given type.

I have some solutions and would like to discuss the merits of each with people who are familiar with Java's object model, since I'm used to a different object model than Java's (CLOS).

Solution #1

Define methods isAThingAAA(obj) and isAThingBBB(obj) in the World class.

These metho开发者_运维知识库ds would call obj.getClass () and check if the returned type is AAA or BBB.

The problem I see with this is having to use "getClass" to implement it. Or is there another way to implement it?

Solution #2

Define methods isAnAAA () and isAnBBB () in the Thing class, implemented to return false. Redefine them in the respective class (AAA.isAnAAA and BBB.isAnBBB) to return true.

This sesms strange because the most abstract class would have knowledge of the existance of its subclasses.

Other suggestions?

Thanks in advance.


How about writing an abstract method in class Thing, and then letting AAA's instances and BBB's instances redefine it. You seem to want to write the isAnXXX methods, maybe you could explain why.

See, using the instance of operator and isAnXXX methods can lead to no polymorphism. And that is not a good thing. You WANT polymorphism, you needs it...gollum,gollum. Also, consider that tomorrow you want to add a CCC class to your World. Your design should guarantee that the World class won't be touched, as in the Open/closed principle

So , summing up, you could do this:

In class Thing:

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

Then override it in child classes

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*/}

}

Then you could fill your Thing[] and do this

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

   }

Each instance of Thing knows how to doSomething, without having to ask for its type.


Another option is to not have these methods at all. These methods are often used to decide what to do. Something like

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

instead it is better for each Thing to know what to do when an action is required. All you should have to call is

thing.method(); // each type know what to do.


You should use instanceof operator

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

EDIT: also Tom's answer should suffice to solve your problem and that is the good practice.


you can use instanceof or 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");
        }
}


The simplest way would be to use Java's built-in instanceof operator. For example:

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

Many people, however, would tell you that using instanceof is symptomatic of poor class design, and that the proper way to achieve different behavior with different subclasses is through polymorphism. The problem here is that it it's very easy in Java to get a class to do something different based on what derived type it is, but it's a bit tricky getting some other object to treat a Thing differently depending upon what derived type it has a handle on. This is the Double Dispatch problem.

It would be ideal if, when handling your Thing object, you could just dispatch the problem to some other methods that would do different things with the Thing depending on what subclass it is, like so:

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
}

In Java, however, which only supports single dispatch, regardless of the actual type of myThing in handleThing(), reactToThing(Thing) will always get called, and you'll never get your unique behavior.

What you need to do to get around this problem is use the Visitor Pattern. This just involves putting some extra code in your Thing class and all its children to give your reactToThing() methods some extra context. So let's say the above methods are all in a class called Visitor. We can rewrite the above to work by first handing the problem off to the Thing object itself, then polymorphism will give us an appropriate context (Thing, AAA, or BBB) for the object to give back to the Visitor.

So, we can rewrite the example above as follows:

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);
  }
}

So why did I need to rewrite the same accept() method in both subclasses? Because otherwise the object would still be in a Thing context when calling the visitor.reactToThing(this) method, and thus we have the same problem as before. By reimplementing it in all three places, the derived class overrides the parent's implementation, and the correct method is called in Visitor.

Seems like a lot of work when all you want to know is what derived class you're working with, but the payoff is extensibility and maintenance. Now you don't need to go around adding if (something instanceof somethingElse) all over the place. Specifically, you'll have less duplicate code you need to maintain if you ever decide you need to change something down the road, like extend Visitor, for example.

Hope that addresses your question.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜