2015-08-13 41 views
0

背景:我经常问自己以下问题,我从来没有找到没有缺陷的答案,我认为这是模块化软件开发中的常见问题。所以我不肯定是否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 
    } 
} 

最后的思考:我不满意这三个解决方案之一100% - 每个人都有缺陷。我更喜欢强制开发人员遵循API的纯粹主义(例如,他不应该能够在任何地方使用单身人士 - 他应该得到数据,如果他需要他们,他必须存储他们 - 否则他必须处理它) 。

StackOverflow问题:有没有其他方法可以“解决”这个问题(也许我错过了这些想法)?

+1

我错过了什么,或者为什么没有你提到使用简单的文件管理和配置,您可以通过Spring通过接口,自动装配强制制定者(或注入自己,因为你可以简单的测试界面)并嘲笑UnitTests。 –

+0

这是另一个解决方案,但对我来说,这不是一个合适的API,因为有人可以访问文件管理器对象并将配置设置为null - et瞧,你有乐趣 – swaechter

+1

虽然这是真的,但我没有看到问题。有人也可以编写一个FileManager实现,抛出随机异常并将其设置在那里,好的。但是,对于那些做最愚蠢的事情的人来说,冥想并不是思考的最佳方式。我总是假设合理的编码员。 –

回答

1

从弗洛里安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; 
     } 
    } 
} 
+0

干得好。就个人而言,我会让Plugin成为一个界面(更容易模拟等),并为需要它的人提供了一个基本的AbstractPlugin,但这不仅仅是一种品味问题。 –