以标准化方式将对象传递给接口/抽象类

时间:2015-08-13 09:36:07

标签: java plugins interface annotations abstract

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

示例:有一个文本编辑器,可以在文本编辑器中加载几个负责文件类型交互的插件(Java插件处理Java代码/文件,C / C ++插件处理C / C ++代码/文件等) - 每个插件都需要配置和访问文件管理器。这些插件必须访问应用程序API才能完成一些有用的东西,而无需重新发明轮子。请记住,例如,应用程序的文件管理器本身也需要数据/对象(例如,具有行分隔符类型的配置,读取模式的类型等。)

问题:当文件管理器还需要配置本身时,如何将配置/文件管理器传递给模块化应用程序中的pugin(基于接口或抽象类)?

要记住的问题/事情:

  • 我们需要将数据传递给文件管理器(配置等)。
  • Java不支持接口和抽象类中的自定义构造函数
  • 注释对象很难测试,往往是所有邪恶的根源
  • 尝试创建一个合适/清晰的API,尽可能地以捣乱的方式滥用/使用API​​

解决方案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不支持接口/抽象类中的自定义构造函数) 。我们使用反射创建插件对象,并将配置和文件管理器传递给构造函数。但是如果我们需要第三个对象会发生什么?

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

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

StackOverflow问题:还有其他方法可以解决问题吗?这个问题(也许是我错过的想法)?

1 个答案:

答案 0 :(得分:1)

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

  • 隐藏插件开发人员所需的所有方法
  • 扩展基本插件类的插件中没有构造函数
  • 轻松添加新变量/方法
  • 适当且干净的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;
        }
    }
}