Как вы заметили, ни одна из трех идей в вопросе не поддерживается (конструктор с определенной сигнатурой в интерфейсе, абстрактный класс, обеспечивающий определенный конструктор, или статический метод в интерфейсе или абстрактном классе)
Однако вы можете определить интерфейс (или абстрактный класс), который является фабрикой для нужного вам типа.
public interface AnInterface {
int fancyComputation();
}
public interface IFooBarFactory<T extends AnInterface> {
T create(int magicNumber);
}
IFooBarFactory имеет 2 конкретные реализации
public class BarFactory implements IFooBarFactory<Bar> {
public Bar create(int magicNumber) {
return new Bar(magicNumber);
}
}
public class FooFactory implements IFooBarFactory<Foo> {
public Foo create(int magicNumber) {
return new Foo(magicNumber);
}
}
Затем используйте шаблон стратегии (https://en.wikipedia.org/wiki/Strategy_pattern). чтобы получить правильный завод. Затем используйте эту фабрику с известным интерфейсом для производства вашего объекта с правильным значением (и любыми дополнительными значениями, которые требуются для производства объекта).
FooBarFactory fooBarFactory = new FooBarFactory();
IFooBarFactory<T> factory = fooBarFactory.createFactory(typeOfAnInterface);
T impl = factory.create(magicNumber);
С конкретными реализациями
public class Bar implements AnInterface {
private final int magicInt;
public Bar(int magicInt) {
this.magicInt = magicInt;
}
public int fancyComputation() {
return magicInt + 2;
}
}
public class Foo implements AnInterface {
private final int magicInt;
public Foo(int magicInt) {
this.magicInt = magicInt;
}
public int fancyComputation() {
return magicInt + 1;
}
}
следующий код:
public static void main(String ... parameters) {
test(Foo.class);
test(Bar.class);
}
private static <T extends AnInterface> void test(Class<T> typeOfAnInterface) {
T impl = createImplForAnInterface(typeOfAnInterface, 10);
System.out.println(typeOfAnInterface.getName() + " produced " + impl.fancyComputation());
}
private static <T extends AnInterface> T createImplForAnInterface(Class<T> typeOfAnInterface, int magicNumber) {
FooBarFactory fooBarFactory = new FooBarFactory();
IFooBarFactory<T> factory = fooBarFactory.createFactory(typeOfAnInterface);
T impl = factory.create(magicNumber);
return impl;
}
отпечатки
Foo produced 11
Bar produced 12
Это дает ряд преимуществ по сравнению с решением с самоанализом или статическими фабриками. Вызывающему не нужно знать, как производить какие-либо объекты, а вызывающему не требуется знать или заботиться о том, является ли метод «правильным» методом для использования для получения правильного типа. Все вызывающие объекты просто вызывают один общедоступный/известный компонент, который возвращает "правильную" фабрику. Это делает ваши вызывающие объекты более чистыми, потому что они больше не тесно связаны с конкретными реализациями AnInterface для типов FooBar. Им нужно только беспокоиться о том, что «мне нужна реализация AnInterface, которая потребляет (или обрабатывает) этот тип». Я знаю, что это означает, что у вас есть два "фабричных" класса. Один для получения правильной фабрики, а другой фактически отвечает за создание конкретных типов Foo и Bar. Однако вы скрываете эту деталь реализации от вызывающих объектов с помощью дополнительного уровня абстракции (см. метод createImplForAnInterface).
Этот подход будет особенно полезен, если вы обычно используете какую-либо форму внедрения зависимостей. Моя рекомендация точно соответствует внедрению с помощью Guice (https://github.com/google/guice/wiki/AssistedInject) или аналогичная идея в Spring (Возможно ли и как сделать Assisted Injection в Spring?).
Это означает, что вам нужно иметь несколько фабричных классов (или правил привязки внедрения зависимостей для Guice), но каждый из этих классов небольшой, простой и простой в обслуживании. Затем вы пишете небольшой тест, который извлекает все классы, реализующие AnInterface, и проверяете, что ваш компонент, реализующий шаблон стратегии, охватывает все случаи (через отражение — я бы использовал класс Reflections в org.reflections:reflections). Это дает вам полезную абстракцию кода, которая упрощает использование этих объектов, уменьшая избыточный код, ослабляя тесную связь компонентов и не жертвуя полиморфизмом.
person
Nathan
schedule
06.11.2015
SampleSource
имеет параметр, расширяющийSampleFactory
. Затем вgetCurrentSample()
вы вызываете эту фабрику образцов, чтобы создать образец, который должен иметь тот же тип, что иSampleFactory
. Итак, создание образца дает вам фабрику образцов? - person Timo   schedule 02.11.2015Sample
и реализуютSampleFactory
... - person Thomas   schedule 02.11.2015new
, и вы не можете создать экземпляр интерфейса. - person Andreas   schedule 02.11.2015T
во время выполнения неизвестен, поэтомуT.class
не будет работать. - person Andreas   schedule 02.11.2015