使用Java泛型接口/抽象类的构造函数

时间:2015-11-02 10:43:21

标签: java generics constructor factory-pattern static-constructor

请注意更新,我的问题没有明确制定。对不起。

我们假设我们有以下代码:

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 interfaceabstract class中的静态方法,可以为AnInterfaceAnAbstractClass的每个实现覆盖(我认为是工厂模式)< / p>

  4. 要走的路是什么?

3 个答案:

答案 0 :(得分:4)

我真的没有看到你的想法。

我觉得这打破了Factory模式的概念,它的目的是让一个方法负责创建单个类see ref的实例。

我宁愿:

  1. 在工厂类中为要构造的每种类型的对象都有一个方法
  2. 并且可能不是在构造函数中具有特定行为,而是在父抽象类中使用一个公共构造函数,并使用一个抽象方法来执行花哨的计算(但这确实是样式首选项)。
  3. 这将产生以下内容:

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

答案 1 :(得分:2)

正如您所指出的那样,问题中的3个想法都不受支持(在接口中具有特定签名的构造函数,强制执行某个构造函数的抽象类,或者在接口或抽象类中的静态方法)

但是,您可以定义一个接口(或抽象类),它是您最终需要的类型的工厂。

public interface AnInterface {
    int fancyComputation();
}
public interface IFooBarFactory<T extends AnInterface> {
    T create(int magicNumber);
}

IFooBarFactory有两个具体的实现

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);

使用conrete实现

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

与内省或静态工厂的解决方案相比,这提供了许多好处。呼叫者不需要知道如何制造任何物体,呼叫者也不需要知道或关心何时方法是正确的&#34;用于检索正确类型的方法。所有呼叫者只需调用一个公共/已知组件,该组件返回&#34;正确的&#34;厂。这使得调用者更加清晰,因为它们不再与FooBar类型的AnInterface的具体实现紧密耦合。他们只需要关注&#34;我需要AnInterface的实现,它会消耗(或处理)这种类型。&#34;我知道这意味着你有两个&#34;工厂&#34;类。一个用于检索正确的工厂,另一个用于创建具体类型Foo和Bar。但是,您通过附加的抽象层从调用者隐藏此实现细节(请参阅createImplForAnInterface方法)。

如果您通常使用某种形式的依赖注入,这种方法将特别有用。我的建议完全符合Guice的辅助注入(https://github.com/google/guice/wiki/AssistedInject)或春天的类似想法(Is it possible and how to do Assisted Injection in Spring?)。

这意味着你需要有几个工厂类(或Guice的依赖注入绑定规则),但这些类都很小,很简单,易于维护。然后你编写一个小测试来检索实现AnInterface的所有类,并验证实现策略模式的组件是否涵盖了所有情况(通过反射 - 我将使用org.reflections中的Reflections类:reflection)。这为您提供了一个可用的代码抽象,通过减少冗余代码,放松组件的紧密耦合,而不是牺牲多态,简化了这些对象的使用。

答案 2 :(得分:2)

听起来你真的在寻找一种解决方案来解决如何在没有一堆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