-2

Обратите внимание на обновления, мой вопрос не был четко сформулирован. Простите за это.Конструктор интерфейса/абстрактного класса с использованием генераторов Java

Давайте предположим, что мы имеем следующий код:

class Foo extends/implements AnAbstractClass/AnInterface { /* to make sure the constructor with int as input is implemented */ 
    Foo(int magicInt) { magicInt + 1; /* do some fancy calculations */ } 
} 

class Bar extends/implements AnAbstractClass/AnInterface { /* to make sure the constructor with int as input is implemented */ 
    Bar(int magicInt) { magicInt + 2; /* do some fancy calculations */ } 
} 

class Factory<T extends/implements AnAbstractClass/AnInterface> { 
    int magicInt = 0; 

    T createNewObject() { 
     return new T(magicInt) // obviously, this is not working (*), see below 
    } 
} 

/* how it should work */ 
Factory<Foo> factory = new Factory<Foo>(); 
factory.createNewObject() // => Foo with magicInt = 1 

Factory<Bar> factory = new Factory<Bar>(); 
factory.createNewObject() // => Bar with magicInt = 2 

В положении (*) я не знаю, что делать. Как я могу убедиться, что реализован конструктор с такой сигнатурой ...(int magicInt)? Я не могу определить

  1. конструктор с определенной сигнатурой в интерфейсе

    interface AnInterface { 
        AnInterface(int magicInt); 
    } 
    
  2. абстрактный класс соблюдения определенного конструктора

    abstract class AnAbstractClass { 
        abstract AnAbstractClass(int magicInt); 
    } 
    

    , и это, очевидно, отсутствует требование о наличии реализованный конструктор в подклассах:

    abstract class AnAbstractClass { 
        AnAbstractClass(int magicInt) {} 
    } 
    
  3. статический метод в an interface или abstract class, которые могут быть переопределены для каждой реализации AnInterface или AnAbstractClass (я думаю о шаблоне завод)

Что такое путь?

+0

Я не понимаю, в чем ваш вопрос ... Что именно вы пытаетесь достичь и почему это не работает? –

+1

Я думаю, что в вашем желаемом коде результата есть что-то странное. 'SampleSource' имеет параметр, который расширяет' SampleFactory'. Затем в 'getCurrentSample()' вы вызываете эту фабрику образцов для создания образца, который должен иметь тот же тип, что и 'SampleFactory'. Итак, создание образца дает вам образец фабрики? – Timo

+1

Ну, так как в интерфейсах допускаются статические методы Java 8. – Flown

ответ

1

Похоже, вы действительно ищете решение о том, как писать общий заводский метод без кучи блоков if/else и без написания одного в каждом классе. Таким образом, следует использовать отражение, как в следующем коде:

interface Interface { 
} 

class Foo implements Interface { 
    Foo(int magicInt) { magicInt = magicInt + 1; /* do some fancy calculations */ } 
} 

class Bar implements Interface { 
    Bar(int magicInt) { magicInt = magicInt + 2; /* do some fancy calculations */ } 
} 

class Factory<T extends Interface> { 
    int magicInt = 0; 

    public T createNewObject(Class<T> typeToMake) { 
     try { 
      T t = createNewObjectWithReflection(typeToMake); 
      return t; 
     } catch (Exception e) { 
      throw new RuntimeException("Construction failed!", e); 
     } 
    } 

    private T createNewObjectWithReflection(Class<T> typeToMake) throws Exception { 
     // find the constructor of type to make with a single int argument 
     Constructor<T> magicIntConstructor = typeToMake.getDeclaredConstructor(Integer.TYPE); 
     // call the constructor with the value of magicInt 
     T t = magicIntConstructor.newInstance(magicInt); 
     return t; 
    } 
} 

/* Name of the class has to be "Main" only if the class is public. */ 
class Ideone 
{ 
    public static void main (String[] args) throws java.lang.Exception 
    { 
     Factory<Foo> fooFactory = new Factory<Foo>(); 
     Foo foo = fooFactory.createNewObject(Foo.class); 
     System.out.println(foo); 

     Factory<Bar> barFactory = new Factory<Bar>(); 
     Bar bar = barFactory.createNewObject(Bar.class); 
     System.out.println(bar); 
    } 
} 

Вы можете run the demo at IDEOne here.

+0

Да, это то решение, которое я ищу. Но разве это уродливо на Java? –

+0

В основном, да.Я отредактировал кодовое сообщение, чтобы немного убрать код, но отражение вообще не очень красивое. – entpnerd

+0

Нет причин для того, чтобы завод сам по себе был общим. Достаточно было бы иметь метод на заводе-изготовителе. Я также думаю, что вы, вероятно, могли бы достичь той же функциональности, используя TypeLiteral с привязкой Factory для Guice, используя assisted-inject. Это делает ваш код немного чище. –

4

Я действительно не вижу, чтобы ваша идея работала.

Я чувствую, что это нарушает концепцию шаблона Factory, который действительно нацелен на то, чтобы иметь метод, ответственный за создание экземпляров одного класса see ref.

я предпочел бы:

  1. один метод в классе фабрики для каждого типа объекта, который вы хотите построить
  2. и, возможно, вместо того, чтобы конкретное поведение в конструкторах, имеют один общий конструктор в родительский абстрактный класс и один абстрактный метод, который делает причудливое вычисление (но это действительно предпочтение стиля).

Что бы привести к чему-то вдоль линий:

abstract class AbstractSample { 
    private int magicInt; 

    public AbstractSample(int magicInt) { 
     this.magicInt = magicInt; 
    } 

    protected int getMagicInt() { 
     return magicInt; 
    } 

    public abstract int fancyComputation(); 

} 

public class Foo extends AbstractSample { 
    public Foo(int magicInt) { 
     super(magicInt) 
    } 

    public int fancyComputation() { 
     return getMagicInt() + 1; 
    } 
} 

public class Bar extends AbstractSample { 
    public Bar(int magicInt) { 
     super(magicInt) 
    } 

    public int fancyComputation() { 
     return getMagicInt() + 2; 
    } 
} 

public class SampleFactory { 
    private int magicInt = 0; 

    public Foo createNewFoo() { 
     return new Foo(magicInt); 
    } 

    public Bar createNewBar() { 
     return new Bar(magicInt); 
    } 
} 

Ответ на предыдущую версию вопросаможет быть удален, если обновленный ответ удовлетворяет Op

Это определенно странно иметь классы, которые расширяют Sample и реализуют SampleFactory ...

я предпочел бы что-то вдоль линий:

class Sample { 
    protected Sample() { /* ... */ } 
} 

interface SampleFactory<T extends Sample> { 
    T createSample(final int i); 
} 

class AccelerationSample extends Sample { 
    public AccelerationSample(final int i) { /* do some fancy int calculations*/ } 
} 

class OrientationSample extends Sample { 
    private OrientationSample (final int i) { /* do some fancy int calculations*/ } 
} 

abstract class SampleSource<T extends Sample> { 
    int magicInt; 
    SampleFactory<T> sampleFactory; 
    T getCurrentSample() { 
     return sampleFactory.createSample(magicInt); 
    } 
} 

class AccelerationSampleSource extends SampleSource<AccelerationSample> { 
    SampleFactory<AccelerationSample> sampleFactory = new SampleFactory<> { 
     public AccelerationSample createSample(final int i) { 
      return new AccelerationSample(i); 
     } 
    } 
} 

class OrientationSampleSource extends SampleSource<OrientationSample> { 
    SampleFactory<OrientationSample> sampleFactory = new SampleFactory<> { 
     public OrientationSample createSample(final int i) { 
      return new OrientationSample(i); 
     } 
    } 
} 

Было бы еще чище использовать именованные заводы, такие как

public AccelerationSampleFactory implements SampleFactory<AccelerationSample> { 
    public AccelerationSample createSample(final int i) { 
     return new AccelerationSample(i); 
    } 
} 

Что вы могли бы использовать в качестве

class AccelerationSampleSource extends SampleSource<AccelerationSample> { 
    SampleFactory<AccelerationSample> sampleFactory = new AccelerationSampleFactory(); 
} 
+0

Действительно, извините ... Не могу определить статические методы в интерфейсе ... Я обновил свой ответ соответственно. (Нет необходимости, чтобы метод был статическим, если вы хотите, чтобы он был общим). – Thomas

+1

(точнее, в java 8, вы можете иметь статические методы в интерфейсах, но определенно не статические методы, возвращающие тип экземпляра) – Thomas

+0

Спасибо за ваше решение, но тогда у меня есть статический заводский метод для каждого класса (в моем случае У меня было бы десятки таких). Я подумал, поэтому появились дженерики. :) –

2

Как вы уже отметили, ни одна из трех идей в вопросе не поддерживается (конструктор с сертификатом в сигнатуре в интерфейсе, абстрактный класс, обеспечивающий выполнение определенного конструктора, или статический метод в интерфейсе или абстрактном классе)

Однако вы можете определить интерфейс (или абстрактный класс), который является Factory для типа, который вы в конечном счете, хотят.

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 

Это обеспечивает ряд преимуществ по сравнению с раствором с самоанализом O r статических заводов. Вызывающему не нужно знать, как изготовить какой-либо из объектов, а также не нужно, чтобы вызывающий вызывал необходимость знать или ухаживать, когда метод является «правильным» методом, используемым для получения правильного типа. Все вызывающие абоненты просто называют один общедоступный/известный компонент, который возвращает «правильную» фабрику. Это делает ваших абонентов более чистыми, потому что они больше не тесно связаны с конкретными реализациями AnInterface для типов FooBar. Им нужно только заботиться о «Мне нужна реализация AnInterface, которая потребляет (или обрабатывает) этот тип». Я знаю, что это означает, что у вас есть два «фабричных» класса. Один для получения правильной фабрики, а другой - для создания конкретных типов Foo и Bar. Однако вы скрываете эту деталь реализации от вызывающих абонентов через дополнительный слой абстракции (см. Метод createImplForAnInterface).

Этот подход будет особенно полезен, если вы обычно используете некоторую форму инъекции зависимости. Моя рекомендация в точности соответствует приложению, полученному от Guice (https://github.com/google/guice/wiki/AssistedInject) или аналогичной идее весной (Is it possible and how to do Assisted Injection in Spring?).

Это означает, что вам нужно иметь несколько фабричных классов (или правила привязки вложений для Guice), но каждый из этих классов является небольшим, простым и простым в обслуживании. Затем вы пишете небольшой тест, который извлекает все классы, которые реализуют AnInterface, и вы проверяете, что ваш компонент, который реализует шаблон стратегии, охватывает все случаи (через отражение - я бы использовал класс Reflections в org.reflections: reflections).Это дает вам полезную абстракцию кода, которая упрощает использование этих объектов за счет сокращения избыточного кода, ослабления жесткой связи компонентов и не жертвуя полиморфизмом.

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