Java SE - 为不同的库模块实现 "plug and play" 的巧妙方法

Java SE - Clever way to implement "plug and play" for different library modules

我正在尝试做一些聪明的事情。我正在创建一个天气应用程序,我们可以在其中将天气 API 替换为另一个天气 API 而不会影响代码库。所以我从一个包含多个模块的 Maven 项目开始。

我有一个包含接口 class 和基础 class 的基础模块。 Interface class 包含对 APIs 的调用(所有调用都是相似的,即使不完全相同)并且 Base class 包含对 APIs 的属性(同样,所有属性都相似,即使不完全相同)。

我有两个天气 API 的每个模块,我们正在测试并计划在我们开发应用程序时为新天气 API 创建更多模块。

最后,我创建了一个核心模块(包括 main)来实现特定模块 class 天气 API 我想测试。

现在,我知道最简单的方法是使用 switch 语句和枚举。但我想知道是否有更聪明的方法来做到这一点。也许使用模式?有什么建议吗?

这是我刚刚描述的结构的图片:

这是 UML 表示:

这对我来说是一个学习的过程。我想了解真正的 Java Guru 如何根据指定的配置实现适当的模块和 class。

感谢您的建议。

I'm trying to do something clever. I am creating a weather application in which we can replace the weather API with another weather API without affecting the code base.

不往下看,这第一句话让我想到了一个plugin architecture design,但是在软件设计的过程中,不能仓促做决定,越拖越有信息,可以做出更明智的决定,现在只是一个想法要记住。

I have a Base module that contains the Interface class and the Base class. The Interface class contains the calls to the APIs (all calls are similar, if not exact) and the Base class contains the properties to the APIs (again, all properties are similar, if not exact).

当不同的模块共享 behaviour/state 时,重构它们并生成基础抽象 classes 和接口是个好主意,这样你就走在了正确的轨道上,但是,如果存在差异,那些不应该重构到基本模块中。这背后的原因很简单,可维护性。如果您开始添加 if 子句或开关来处理这些差异,那么您只是引入了模块之间的耦合,并且无论何时 add/modify 其他模块,您都必须始终在基础模块中进行更改,而这不是可取的。

Open/Closed principle form the SOLID principles 反映了这一点,它指出 class 应该对扩展开放但对修改关闭。

因此,在将通用行为重构到基本模块中之后,每个新的 API 都应该像您所做的那样扩展基本模块。

Finally, I have created a Core module (includes main) to implement the specific module class for the weather API I want to test.

Now, I know the simplest way to do this would be to use a switch statement and enumeration. But I want to know if there is a more clever way to do this. Maybe using a Pattern? Any suggestions?

的确,利用一个开关,让它工作,但它根本不是一个干净的设计,出于同样的原因,当添加、修改或删除模块时,也需要修改这个模块,并且此代码也可能会中断。

一个可能的解决方案是将此职责委派给一个新组件并使用像 Abstract Factory 这样的创建设计模式,它将提供一个接口来实例化组件而无需指定其 classes.

至于架构,到目前为止,插件架构仍然有意义,但是如果不同的模块扩展基础合约添加更多功能呢?一种选择是使用 Facade pattern 来调整模块调用并提供实现客户期望的接口的输出。

但话又说回来,根据提供的详细信息,这是我建议的解决方案,但应仔细和更详细地研究该方案,以便能够确保这些是适用于工作,并致力于他们。

除了萨尔瓦多...

实现插件架构 Java 的 Jar File Specification provides support for service provider interfaces (SPI) 以及如何查找它们。

截至 Java 1.6。你可以使用 ServiceLoader to lookup service providers. For Java 1.5. and less you must do it on your own or use a library. E.g. commons-discovery.

用法非常简单。在您的情况下,在每个插件模块中放置一个 META-INF/services/com.a2i.weatherbase.IWeather 文件。

Weather Forecast IO 模块中,文件应仅包含一行

 com.a2i.weatherforecastio.ForecastIO

该行必须是 IWeather 实现的全限定名 class。

对另一个模块执行相同的操作,您可以通过 ServiceLoader.

加载实现
ServiceLoader<IWeather> weatherServicesLoader = ServiceLoader.load(IWeather.class);
Iterator<IWeather> weatherServices = weatherServicesLoader.iterator();

现在取决于您的运行时间class路径将找到多少服务。尝试从 class 路径和 运行 您的应用程序中添加和删除模块 jar 档案。

编辑 我写了一篇关于标准 java 的可插拔架构的博客。参见 http://www.link-intersystems.com/blog/2016/01/02/a-plug-in-architecture-implemented-with-java/

源代码也可以在 https://github.com/link-intersystems/blog/tree/master/java-plugin-architecture

一个解决方案是您必须定义具有所有标识的通用操作的通用接口。 extensions/plugins 需要实现该接口,并且必须为常见操作提供实现。

您可以使用抽象工厂设计模式根据输入参数在运行时挂接准确的实现。

接口和抽象 类 在这种情况下总是好的,谢谢。