2010-05-07 4 views
4

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

У меня есть три разных способа, но никто не кажется идеальным. Эти тестовые версии допускают только строки и целые числа, и единственная операция добавляется. Добавление целых чисел приводит к сумме целочисленных значений, добавление строк объединяет строки и добавление целого числа в строку преобразует целое число в строку и объединяет его со строкой. Окончательная версия будет иметь больше типов (парные разряды, массивы, объекты, похожие на 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 общественности Строка ToString(); public DynObject2 add (DynObject2 d); }

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

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

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

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

+1

Кстати, если мои знания Java не мешают мне, 'Class c = d.getClass(); if (c == DynInteger2.class) 'может быть записано как' if (d instanceof DynInteger2) ' – Thomas

ответ

2

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

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

0

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

Смежные вопросы