背景:我经常问自己以下问题,我从未找到没有缺陷的答案,我认为这是模块化软件开发中的常见问题。所以我不太确定programmers.stackexchange.com是否更合适 - 但这个问题完全集中在Java上,我想要一个Java解决方案:
示例:有一个文本编辑器,可以在文本编辑器中加载几个负责文件类型交互的插件(Java插件处理Java代码/文件,C / C ++插件处理C / C ++代码/文件等) - 每个插件都需要配置和访问文件管理器。这些插件必须访问应用程序API才能完成一些有用的东西,而无需重新发明轮子。请记住,例如,应用程序的文件管理器本身也需要数据/对象(例如,具有行分隔符类型的配置,读取模式的类型等。)
问题:当文件管理器还需要配置本身时,如何将配置/文件管理器传递给模块化应用程序中的pugin(基于接口或抽象类)?
要记住的问题/事情:
解决方案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问题:还有其他方法可以解决问题吗?这个问题(也许是我错过的想法)?
答案 0 :(得分:1)
在Florian Schaetz的意见之后,我最终得到了这个我非常喜欢的解决方案:
代码:
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;
}
}
}