以标准化方式将对象传递给 interfaces/abstract 类

Pass objects in a standardized way to interfaces/abstract classes

背景: 我经常问自己以下问题,但我从来没有找到完美无缺的答案,我认为这是模块化软件开发中的常见问题。所以我不太确定 programmers.stackexchange.com 是否更合适 - 但这个问题完全集中在 Java 上,我想要一个 Java 解决方案:

示例: 有一个文本编辑器加载了几个插件,这些插件负责在文本编辑器中进行文件类型交互(Java 插件句柄 Java code/files, C/C++ 插件句柄 C/C++ code/files 等) - 每个插件都需要配置和访问文件管理器。这些插件必须访问应用程序 API 才能在不重新发明轮子的情况下做一些有用的事情。请记住,例如应用程序的文件管理器也需要 data/objects 本身(例如带有行分隔符类型、读取模式类型等的配置)。

问题:当文件管理器也需要自己配置?

Problems/Things 谨记:

解决方案1:通过插件中的注解使用依赖注入,这样我们就可以获取配置和文件管理器并在我们的插件中使用它们。这是最流畅的解决方案,但并不容易测试。

public class JavaPlugin implements Plugin
{
    @Autowired
    Configuration configuration;

    @Autowired
    FileManager filemanager;

    public String processText(String rawtext)
    {
        // Do stuff and access the configuration/file manager
    }
}

解决方案 2: 将文件管理器设为单例...但是等等,我们如何将配置传递给单例?制作另一个采用这些参数的静态方法?那不再是真正的单身人士了,真正的地狱 API.

public class JavaPlugin implements Plugin
{
    public String processText(String rawtext)
    {
        FileManager filemanager = FileManager.getInstance();
        Configuration configuiration = filemanager.getConfiguration();
        // Do stuff and use the configuration/file manager
    }
}

public class FileManager
{
    private static FileManager filemanager;

    private Configuration configuration;

    private FileManager(Configuration configuration)
    {
        this.configuration = configuration;
    }

    public static FileManager getIntialInstance(Configuration configuration)
    {
        if(filemanager == null)
        {
            filemanager = new FileManager(configuration);
        }
        return filemanager;
    }

    public static FileManager getInstance()
    {
        return filemanager;
    }

    public Configuration getConfiguration()
    {
        return configuration;
    }
}

解决方案 3: 只需告诉所有插件开发人员实现一个将配置和文件管理器作为参数的构造函数(因为 Java 不支持自定义构造函数在 interface/abstract class 中)。我们使用反射创建插件对象,并将配置和文件管理器传递给构造函数。但是如果我们需要第三个对象会怎样呢?

public class JavaPlugin implements Plugin
{
    public(Configuration configuration, FileManager filemanager)
    {
        // Save them
    }

    public String processText(String rawtext)
    {
        FileManager filemanager = FileManager.getInstance();
        Configuration configuiration = filemanager.getConfiguration();
        // Do stuff and use the configuration/file manager
    }
}

public class PhpPlugin implements Plugin
{
    public PhpPlugin()
    {
        // Oh dear you just broke your plugin....
    }

    public String processText(String rawtext)
    {
        // Do stuff and use the configuration/file manager
    }
}

最终想法: 我对这 3 种解决方案中的一种不是 100% 满意 - 每个人都有缺陷。我更喜欢强迫开发人员遵循 API 的纯粹主义(例如,他不应该在任何地方使用单例 - 他应该获取数据,如果他需要它们,他必须存储它们 - 否则他必须处理它)。

Whosebug 问题: 还有其他方法可以解决 "solve" 这个问题(也许我错过了想法)?

在听取了 Florian Schaetz 的意见后,我最终得到了这个我非常喜欢的解决方案:

  • 对插件开发者隐藏所有必需的方法
  • 扩展基本插件的插件中没有构造函数class
  • 易于添加新的variables/methods
  • 得体干净API

代码:

import java.util.ArrayList;

public class PluginLoader
{
    public static void main(String args[])
    {
        new PluginLoader();
    }

    public PluginLoader()
    {
        Configuration configuration = new Configuration("Processed text from plugin: ");
        FileManager filemanager = new FileManager(configuration);
        ArrayList<Plugin> plugins = new ArrayList<>();

        Plugin plugin1 = new JavaPlugin();
        plugin1.setConfiguration(configuration);
        plugin1.setFileManager(filemanager);
        plugins.add(plugin1);

        Plugin plugin2 = new PhpPlugin();
        plugin2.setConfiguration(configuration);
        plugin2.setFileManager(filemanager);
        plugins.add(plugin2);

        for(Plugin plugin : plugins)
        {
            System.out.println(plugin.getOutputName());
        }       
    }

    public class JavaPlugin extends Plugin
    {
        public String getOutputName()
        {
            return getFileManager().getConfiguration().getText() + "JavaPlugin";
        }
    }

    public class PhpPlugin extends Plugin
    {
        public String getOutputName()
        {
            return getFileManager().getConfiguration().getText() + "PhpPlugin";
        }
    }

    public abstract class Plugin
    {
        private Configuration configuration;

        private FileManager filemanager;

        public abstract String getOutputName();

        public void setConfiguration(Configuration configuration)
        {
            if(this.configuration == null)
            {
                this.configuration = configuration;
            }
        }

        public void setFileManager(FileManager filemanager)
        {
            if(this.filemanager == null)
            {
                this.filemanager = filemanager;
            }
        }

        public Configuration getConfiguration()
        {
            return configuration;
        }

        public FileManager getFileManager()
        {
            return filemanager;
        }
    }

    public class FileManager
    {
        private final Configuration configuration;

        public FileManager(Configuration configuration)
        {
            this.configuration = configuration;
        }

        public Configuration getConfiguration()
        {
            return configuration;
        }
    }

    public class Configuration
    {
        private final String text;

        public Configuration(String text)
        {
            this.text = text;
        }

        public String getText()
        {
            return text;
        }
    }
}