AbstractFactory с универсальными типами в Java: проблема проектирования - PullRequest
1 голос
/ 23 сентября 2011

У меня есть следующие 2 интерфейса в соответствии с абстрактным шаблоном фабрики:

public interface GenericObjectInterface<T extends Number>{
    public T getResult();
}
public interface AbstractFactoryInterface{
    public <T extends Number> GenericObjectInterface<T> createGenericObject();
}

У меня есть абстрактный класс, реализующий GenericObject, но он все еще не знает о конкретном типе (он выполняет только общие операции над Number):

public abstract class GenericAbstractClass<T extends Number> implements GenericObjectInterface<T>{   } 

Затем у меня есть ряд конкретных расширений классов, которые выполняют подстановку общих параметров:

public class IntegerObject extends GenericAbstractClass<Integer>{
     public Integer getResult(){}
}
....

Теперь внутри реализации фабрики я создаю конкретный тип, который реализует GenericObjectInterface, но потерял свой универсальный параметр:

public class ConcreteFactory{
    public <T extends Number> GenericObjectInterface<T> greateGenericObject(Class<T> c){
         if (c.class.isInstance(Integer.class)){
             IntegerObject obj = new IntegerObject();
             //I would like to return obj
             GenericObjectInterface<T> a = new IntegerObject(); //errror
             GenericAbstractClass<T> a = new IntegerObject(); //errror

             return a;
          }else if (c.class.isInstance(Double.class)){
          }
    }
}

Я хотел бы вернуть obj, который реализует GenericObjectInterface, но я не знаю, как я могу это сделать. как я могу решить это?

Я привык абстрагировать фабрику, но я никогда не использовал ее с генериками. Я делаю некоторые ошибки в интерпретации паттерна?

Ответы [ 4 ]

3 голосов
/ 23 сентября 2011

Если ваш метод возвращает IntegerObject, почему бы вам просто не вернуть GenericObjectInterface<Integer>?Вы уже знаете тип параметра.

В этом случае просто добавьте универсальный параметр к AbstractFactoryInterface:

public interface AbstractFactoryInterface<T extends Number> { ... }

public class ConcreteFactory implements AbstractFactoryInterface<Integer> { ... }

В вашей реализации тип T будет выведениз задания, и, таким образом, вы можете сделать это:

 GenericObjectInterface<Double> g = new ConcreteFactory().greateGenericObject();

В этом случае T будет Double, но вы будете использовать Integer внутри, что приведет к:

GenericObjectInterface<Double> a = new IntegerCell(); 

Поскольку компилятор не может гарантировать, что T всегда будет иметь тип Integer, он не позволит вам выполнить это назначение.

1 голос
/ 23 сентября 2011

литье здесь прекрасно. если c==Integer.class, то T=Integer, приведение GOI<Object> к GOI<T> абсолютно правильно. Это проверенное приведение, потому что вы проверили T=Integer перед приведением, поэтому неконтролируемое предупреждение может быть законно подавлено.

1 голос
/ 23 сентября 2011

Как правило, фабрики не реализованы как универсальные, потому что вы не можете проверить тип универсального, чтобы определить тип создаваемого объекта (вы не можете сделать T.getClass), поэтому пример @ Mark заставляет классбыть передан в качестве аргумента.

Я думаю, что чаще всего у вас будет несколько конкретных заводов.Один для каждого типа номера, который вы намереваетесь поддерживать.

public interface AbstractFactoryInterface<T extends Number> {
    public GenericObjectInterface<T> createGenericObject();
}


class IntegerFactory implements AbstractFactoryInterface<Integer>...
class LongFactory implements AbstractFactoryInterface<Long>...

Затем вы можете создать карту...

Map<Class, AbstractFactoryInterface> myMap = ...;
myMap.put(Integer.class, new IntegerFactory());
myMap.put(Long.class, new LongFactory ());
1 голос
/ 23 сентября 2011

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

Подумайте об этом следующим образом: сможете ли вы сделать это?

public class ConcreteListFactory {
    public <T> List<T> createList() {
        return new ArrayList<String>();
    }
}

Что, если вызывающая сторона хотела List<Integer>?

Если вы хотите, чтобы ваша фабрика возвращала обобщенный тип, ваш конкретный класс должен принимать параметр типа.В противном случае ваш фабричный метод возвратит GenericObjectInterface<Integer>.

В качестве альтернативы, вы можете сделать так, чтобы ваш метод принял токен типа (Integer.class).Например:

public <T extends Number> GenericObjectInterface<T> createGenericObject(Class<T> clazz) {
    if ( clazz.equals(Integer.class) ) {
        return (GenericObjectInterface<T>) new IntegerObject();
    }
} 

Это приведет к непроверенному предупреждению о сотворении, но вы можете доказать себе, что оно безопасно, и, таким образом, подавить предупреждение или проигнорировать его.

...