Java 使用反射创建所有子类的对象列表是否合适?

Java 使用反射创建所有子类的对象列表是否合适?,java,reflection,Java,Reflection,在我的程序中,不同的功能被分为不同的模块,可能有数百个模块, 每个模块都是抽象模块类的一个子类 看起来像这样的 public abstract class Module { public final String name; public Module(String name){ this.name = name; } public abstract void execute(); } public class Mod1 extends M

在我的程序中,不同的功能被分为不同的模块,可能有数百个模块, 每个模块都是抽象模块类的一个子类 看起来像这样的

public abstract class Module {

    public final String name;

    public Module(String name){
        this.name = name;
    }

    public abstract void execute();
}
public class Mod1 extends Module{
    public Mod1() {
        super("Mod1");
    }

    @Override
    public void execute() {
        //Do something
    }
}
有这样的子类

public abstract class Module {

    public final String name;

    public Module(String name){
        this.name = name;
    }

    public abstract void execute();
}
public class Mod1 extends Module{
    public Mod1() {
        super("Mod1");
    }

    @Override
    public void execute() {
        //Do something
    }
}
我需要列出所有模块的实例,但这样做有点乏味,因为我的程序可能有数百个模块,可能很难调试(我可能会错过一些lol)

private static final List MODULES=new ArrayList();
公共void init(){
MODULES.add(新Mod1());
MODULES.add(新Mod2());
MODULES.add(新Mod3());
MODULES.add(新Mod4());
MODULES.add(新Mod5());
}
因此,我认为使用反射可能是一种可行的方法,但在快速的谷歌搜索之后,我发现很多人不喜欢生产代码中的反射,所以我来这里询问,这是否是使用反射的适当情况,或者是否有任何理由或设计更改来避免在这种特殊情况下使用反射

编辑:该列表将用于gui中的渲染模块或从其他用户界面(如命令)调用模块的执行方法(只需找到具有匹配名称的模块并执行它)


注意:这并不是我的代码实际的样子,而是一个高度简化的版本,它给出了类似的想法

对于一种最省力的方法,java提供了内置类

使用该类,您可以获得服务类(在本例中为
模块
)的所有可用实现,如

请注意,我强调了上一句中可用的术语。事实上,
ServiceLoader
实现不执行任何高级类层次结构查找,而是依赖名为等于
模块
类的规范名称的特定资源文件

META-INF/services/com.example.project.Module

com.example.project.impl1.Module1
com.example.project.impl2.Module2
其中,文件的每一行都引用了
模块的可用实现


现在,
ServiceLoader
是的一个实现,它不一定完全符合您的用例。然而,由于其服务加载和提供的思想相当简单,您可以轻松地构建自己的变体

public class JsonServiceLoader<S> {

    private Class<S> service;
    private Map<String, String> serviceIdentifiers;

    public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
        Map<String, String> serviceIdentifiers = new HashMap<>();
        String name = "META-INF/json-services/" + service.getCanonicalName();
        // TODO check for null references where necessary
        for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
            try (InputStream resource = resources.next().openStream()) {
                for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
                    serviceIdentifers.merge(
                        identifer.getKey(),
                        identifer.getValue(),
                        (value1, value2) -> throw new ServiceConfigurationError(
                            "duplicate service identifier '" + identifier.getKey() + "'"
                        );
                    );
                }
            }
        }
        return new JsonServiceLoader<>(service, serviceIdentifiers);
    }

    private static Map<String, String> parseJson(InputStream resource) {
        // TODO parse JSON data from the given stream using your favourite JSON facility
        /*
         * If you want to use a different style of resources, e.g. XML, this is the only
         * location you have to change (you might want to rename 'json-services' though).
         */
        return new HashMap<>();
    }

    private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
        this.service = service;
        this.serviceIdentifiers = serviceIdentifiers;
    }

    public Set<String> getServiceIdentifiers() {
        return Collections.unmodifiableSet(serviceIdentifiers.keySet());
    }

    public S getService(String name) {
        String className = serviceIdenfiers.get(name);
        if (null == className) {
            throw new IllegalArgumentException("invalid service identifier '" + name + "'");
        }
        // TODO improve error handling
        try {
            return service.cast(Class.forName(className).newInstance());
        } catch(Exception exc) {
            throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
        }
    }

}
允许将来扩展

此时,
模块
类也不再需要知道它的名称(类成员
名称
),因为您可以随时向服务加载程序请求适当的实例。(如果您在代码中的某个位置这样做,您将已经知道刚才询问的名称。)如果需要,您还可以向这个
JsonServiceLoader
添加更多逻辑,例如缓存


最终,,这完全取决于您希望在
模块
周围处理多少信息,希望
模块
实现处理多少信息,以及希望服务框架处理多少信息。

也许您可以将列表传递到父类的构造函数中,并将子类本身添加到构造函数方法中的列表。 就这样

public abstract class Module {

    public final String name;

    public Module(String name){
        this.name = name;
    }

    public abstract void execute();
}
public class Mod1 extends Module{
    public Mod1() {
        super("Mod1");
    }

    @Override
    public void execute() {
        //Do something
    }
}
公共抽象类模块{
公共最终字符串名;
公共模块(字符串名称、列表){
this.name=名称;
列表。添加(此);
}
公共抽象void execute();
}
private static final List MODULES=new ArrayList();
公共void init(){
新Mod1(模块);
}

这种特殊情况可以通过使用。然而,由于我们不知道“实例列表”的用途,这感觉它也可能是一个。该列表将用于不同类型的东西,如在gui中呈现模块或从其他用户界面(如命令)调用模块的执行方法(只需找到具有匹配名称的模块并执行它)这只会在
模块#init
中的每行中保存几个字节的代码。然而,这个问题的目的是找到一种解决方案,它不依赖于修改
模块
类来定位其实现。