Объектная модель Java и методы, идентифицированные типом - PullRequest
2 голосов
/ 23 июля 2011

Я пишу приложение, в котором есть несколько классов, которые представляют вещи в Мире.

Мир представлен массивом объектов (класса, от которого наследуются все классы объектов, существующих в мире - назовем это Thing).

class World {
  Thing[] myObjects;
}

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

В какой-то момент мне нужно знать, относится ли объект в данной позиции к указанному типу.

У меня есть несколько решений, и я хотел бы обсудить достоинства каждого из них с людьми, знакомыми с объектной моделью Java, поскольку я привык к объектной модели, отличной от Java (CLOS).

Решение № 1

Определите методы isAThingAAA (obj) и isAThingBBB (obj) в классе World.

Эти методы будут вызывать obj.getClass () и проверять, является ли возвращаемый тип AAA или BBB.

Проблема, с которой я сталкиваюсь, заключается в том, чтобы использовать «getClass» для ее реализации. Или есть другой способ реализовать это?

Решение № 2

Определить методы isAnAAA () и isAnBBB () в классе Thing, реализованные для возврата false. Переопределите их в соответствующем классе (AAA.isAnAAA и BBB.isAnBBB), чтобы вернуть true.

Это странно, потому что самый абстрактный класс знал бы о существовании своих подклассов.

Другие предложения?

Заранее спасибо.

Ответы [ 5 ]

4 голосов
/ 23 июля 2011

Как насчет написания абстрактного метода в классе Thing, а затем разрешения экземплярам AAA и BBB переопределить его.Кажется, вы хотите написать методы isAnXXX, может быть, вы могли бы объяснить, почему.

Видите, использование оператора instance of и методов isAnXXX не может привести к полиморфизму.И это не очень хорошая вещь.Вы хотите полиморфизм, вам это нужно ... Gollum, Gollum.Кроме того, учтите, что завтра вы хотите добавить CCC класс к вашему World.Ваш дизайн должен гарантировать, что класс World не будет затронут, как в Принципе открытия / закрытия

Итак, подводя итог, вы можете сделать это:

В классе Thing:

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, без необходимости запрашивать его тип.

3 голосов
/ 23 июля 2011

Другой вариант - вообще не использовать эти методы.Эти методы часто используются, чтобы решить, что делать.Что-то вроде

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.
1 голос
/ 23 июля 2011

Вы должны использовать instanceof оператор

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

РЕДАКТИРОВАТЬ: также ответа Тома должно быть достаточно для решения вашей проблемы, и это хорошая практика.

0 голосов
/ 23 июля 2011

Самый простой способ - использовать встроенный в Java оператор instanceof.Например:

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

Многие люди, однако, скажут вам, что использование instanceof является симптомом плохого проектирования классов, и что правильный способ достижения различного поведения с различными подклассами - через полиморфизм.Проблема здесь в том, что в Java очень просто заставить класс сделать что-то другое в зависимости от того, какой это производный тип, но немного сложно заставить какой-то другой объект обрабатывать Thing по-разному в зависимости от того, какой производный тип имеет дескрипторна.Это проблема Double Dispatch .

Было бы идеально, если бы при обработке вашего объекта 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, которая поддерживает только одну диспетчеризацию, независимо от фактического типа myThing в handleThing (), всегда будет вызываться responseToThing (Thing),и вы никогда не получите свое уникальное поведение.

Что вам нужно сделать, чтобы обойти эту проблему, так это использовать Шаблон посетителя .Это просто включает в себя добавление некоторого дополнительного кода в ваш класс 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 () в обоих подклассах?Потому что в противном случае объект все равно находился бы в контексте Thing при вызове метода visitor.reactToThing (this), и, таким образом, у нас та же проблема, что и раньше.Реализовав его во всех трех местах, производный класс переопределяет реализацию родителя, и в Visitor вызывается правильный метод.

Похоже, много работы, когда все, что вы хотите знать, это какой у вас производный классработать с, но выигрыш в расширяемости и обслуживании.Теперь вам не нужно прибегать к добавлению if (что-то вроде instanceElse) повсеместно.В частности, у вас будет меньше дублирующегося кода, который необходимо поддерживать, если вы когда-нибудь решите, что вам нужно что-то изменить в будущем, например, расширить Visitor.

Надеюсь, что это ответ на ваш вопрос.

0 голосов
/ 23 июля 2011

вы можете использовать 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");
        }
}
...