Лучшая практика Java для разрешения типов во время выполнения - PullRequest
4 голосов
/ 07 мая 2010

Я пытаюсь определить класс (или набор классов, которые реализуют один и тот же интерфейс), который будет вести себя как свободно типизированный объект (например, JavaScript). Они могут содержать любые данные и операции с ними зависят от базового типа.

У меня это работает тремя разными способами, но ни один не кажется идеальным. Эти тестовые версии допускают только строки и целые числа, и единственной операцией является добавление. Добавление целых чисел приводит к сумме целочисленных значений, добавление строк объединяет строки, а добавление целого числа в строку преобразует целое число в строку и объединяет ее со строкой. В окончательной версии будет больше типов (Doubles, Arrays, JavaScript-подобные объекты, где новые свойства могут быть добавлены динамически) и больше операций.

Путь 1:

public interface DynObject1 {
  @Override public String toString();
  public DynObject1 add(DynObject1 d);
  public DynObject1 addTo(DynInteger1 d);
  public DynObject1 addTo(DynString1 d);
}


public class DynInteger1 implements DynObject1 {
  private int value;

  public DynInteger1(int v) {
    value = v;
  }

  @Override
  public String toString() {
    return Integer.toString(value);
  }

  public DynObject1 add(DynObject1 d) {
    return d.addTo(this);
  }

  public DynObject1 addTo(DynInteger1 d) {
    return new DynInteger1(d.value + value);
  }

  public DynObject1 addTo(DynString1 d)
  {
    return new DynString1(d.toString()+Integer.toString(value));
  }
}

... и аналогичные для DynString1

Способ 2: открытый интерфейс DynObject2 { @Override public String toString (); public DynObject2 add (DynObject2 d); } * +1010 *

public class DynInteger2 implements DynObject2 {
  private int value;

  public DynInteger2(int v) {
    value = v;
  }

  @Override
  public String toString() {
    return Integer.toString(value);
  }

  public DynObject2 add(DynObject2 d) {
    Class c = d.getClass();

    if(c==DynInteger2.class)
    {
      return new DynInteger2(value + ((DynInteger2)d).value);
    }
    else
    {
      return new DynString2(toString() + d.toString());
    }
  }
}

... и аналогичные для DynString2

Путь 3:

public class DynObject3 {

  private enum ObjectType {
    Integer,
    String
  };

  Object value;
  ObjectType type;

  public DynObject3(Integer v) {
    value = v;
    type = ObjectType.Integer;
  }

  public DynObject3(String v) {
    value = v;
    type = ObjectType.String;
  }

  @Override
  public String toString() {
    return value.toString();
  }

  public DynObject3 add(DynObject3 d)
  {
    if(type==ObjectType.Integer && d.type==ObjectType.Integer)
    {
      return new DynObject3(Integer.valueOf(((Integer)value).intValue()+((Integer)value).intValue()));
    }
    else
    {
      return new DynObject3(value.toString()+d.value.toString());
    }
  }
}

С помощью логики if-else я мог бы использовать value.getClass () == Integer.class вместо сохранения типа, но с большим количеством типов я бы изменил это, чтобы использовать оператор switch, а Java не позволяет использовать switch Классы.

В любом случае ... Мой вопрос: каков наилучший способ сделать что-то подобное?

Ответы [ 2 ]

2 голосов
/ 07 мая 2010

То, что вы пытаетесь сделать, называется двойная отправка .Вы хотите, чтобы вызываемый метод зависел как от типа времени выполнения вызываемого объекта, так и от типа времени выполнения его аргумента.

Java и другие производные C поддерживают только одну диспетчеризацию, поэтому вам нужнаkludge, как шаблон посетителя , который вы использовали в варианте 1. Это наиболее распространенный способ его реализации.Я бы предпочел этот метод, потому что он не использует отражения.Кроме того, он позволяет вам хранить каждый случай в своем собственном методе, без необходимости большого метода «коммутатора» для диспетчеризации.

0 голосов
/ 07 мая 2010

Я бы выбрал второй вариант, с третьим, я бы лучше использовал дженерики, чтобы вы не полагались на этот Enum. И с первым вариантом вы могли бы реализовать методы на всю оставшуюся жизнь. В любом случае вы можете использовать оператор instanceof для сопоставления классов.

...