Home > Back-end >  Reflection instead of Factory pattern?
Reflection instead of Factory pattern?

Time:11-03

Here is how I managed to constructor objects at runtime based on the subclass I want to invoke (to avoid having to update a Factory class every time I want to add a new subclass):

import java.lang.reflect.InvocationTargetException;

public class Main{
    
    private static final class X{}
    
    private static abstract class A{
        
        public A(X x){}
        
        public static A newInstance(Class<? extends A> subType, X x) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
            
            return subType.getConstructor(X.class).newInstance(x);
            
        }
        
    }
    
    private static final class A_1 extends A{
        
        public A_1(X x){
            
            super(x);
            //...
            
        }
        
    }
    
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
        
        for(int i = 0; i < 1000; i  ){
            X x = new X();
            long t = System.currentTimeMillis();
            A a = A.newInstance(A_1.class, x);
            t = System.currentTimeMillis() - t;
            System.out.println(t);
        }
        
    }
    
}

At first I thought it would be slow but I'm getting 1ms for the first iteration and 0 for any consecutive instantiation.

Can I expect a similar behavior once my application is deployed (Here I simplified the process but it's hardly any harder in the context of my application).

What are the pros and cons of doing such a technique?

CodePudding user response:

Reflection is almost always the worst way to do things.

  • Reflection is slow. It usually cannot be optimized at runtime by the just-in-time compiler.
  • Reflection is bug prone. The compiler cannot verify correctness of reflection code. In your case, the compiler can’t guarantee that subType has a constructor which takes a single X argument.
  • Reflection is hard to follow in code and thus hard to maintain.

In your case, you want a Function that represents the subclass constructor:

public static A newInstance(Function<? super X, ? extends A> constructor, X x) {
        
    return constructor.apply(x);
}

You would call that method with a method reference:

A a = newInstance(A_1::new, x);

CodePudding user response:

Definitely, reflection is not the base case (but it definitely works). I offer to use Factory to hold all factories methods to create required types. Just add the ability to add new factories to the implementation without changing it.

I offer a solution without changing your main API public static <T extends Base> T newInstance(Class<T> subType, X x).

public class Main {

    public static void main(String[] args) throws Exception {
        Factory.add(OneBase.class, OneBase::new);
        Factory.add(TwoBase.class, TwoBase::new);

        OneBase oneBase = Factory.newInstance(OneBase.class, new X());
        TwoBase twoBase = Factory.newInstance(TwoBase.class, new X());
    }

}

final class Factory {

    private static final Map<Class<? extends Base>, Function<X, ? extends Base>> FACTORIES = new HashMap<>();

    public static void add(Class<? extends Base> subType, Function<X, ? extends Base> factory) {
        FACTORIES.put(subType, factory);
    }

    public static <T extends Base> T newInstance(Class<T> subType, X x) {
        return (T)FACTORIES.get(subType).apply(x);
    }

}

final class X {}

abstract class Base {

    protected Base(X x) {}

}

final class OneBase extends Base {

    public OneBase(X x) {
        super(x);
    }

}

final class TwoBase extends Base {

    public TwoBase(X x) {
        super(x);
    }

}
  • Related