【问题标题】:Reflection instead of Factory pattern?反射而不是工厂模式?
【发布时间】:2021-11-02 16:54:52
【问题描述】:

以下是我如何在运行时根据我想要调用的子类来构造对象(以避免每次我想添加新的子类时都必须更新 Factory 类):

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

起初我认为它会很慢,但第一次迭代得到 1 毫秒,任何连续实例化得到 0。

部署我的应用程序后,我能否期待类似的行为(这里我简化了流程,但在我的应用程序上下文中几乎没有任何困难)。

做这种技术有什么好处和坏处?

【问题讨论】:

  • 一毫秒实际上是相当长的时间。您可能希望以纳秒为单位进行测量。
  • 缺点:没有编译时检查 A_1 是否存在匹配的构造函数。缺点:反射构造函数的参数类型必须完全是X(不仅仅是与X兼容的类型)。

标签: java reflection factory


【解决方案1】:

反思几乎总是最糟糕的做事方式。

  • 反射很慢。即时编译器通常无法在运行时对其进行优化。
  • 反射很容易出错。编译器无法验证反射代码的正确性。在您的情况下,编译器无法保证 subType 有一个接受单个 X 参数的构造函数。
  • 反射在代码中难以遵循,因此难以维护。

在您的情况下,您需要一个代表子类构造函数的Function

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

您可以使用方法引用调用该方法:

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

【讨论】:

    【解决方案2】:

    当然,反射不是基本情况(但它确实有效)。我提议使用Factory 来保存所有工厂方法来创建所需的类型。只需添加在实现中添加新工厂的功能,而无需更改它。

    我提供了一个解决方案,无需更改您的主要 API public static &lt;T extends Base&gt; T newInstance(Class&lt;T&gt; 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);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多