4

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

我有一个包含 Interface 类和 Base 类的 Base 模块。Interface 类包含对 API 的调用(所有调用都是相似的,如果不准确的话),Base 类包含 API 的属性(同样,所有属性都是相似的,如果不准确的话)。

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

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

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

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

在此处输入图像描述

这是 UML 表示:

在此处输入图像描述

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

谢谢你的建议。

4

3 回答 3

3

我正在尝试做一些聪明的事情。我正在创建一个天气应用程序,我们可以在其中用另一个天气 API 替换天气 API,而不会影响代码库。

不用往下看,这第一句话让我想到了插件架构设计,但是在软件设计的过程中,决不能仓促,越拖延,你掌握的信息就越多,可以做出更明智的决定,现在只是一个需要牢记的想法。

我有一个包含 Interface 类和 Base 类的 Base 模块。Interface 类包含对 API 的调用(所有调用都是相似的,如果不准确的话),Base 类包含 API 的属性(同样,所有属性都是相似的,如果不准确的话)

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

这反映在SOLID 原则中的Open/Closed 原则中,该原则规定一个类应该对扩展开放但对修改关闭。

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

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

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

确实,使用开关可以使其工作,但它根本不是一个干净的设计,出于与以前相同的原因,在添加、修改或删除模块时,也需要修改此模块,并且此代码可能潜在地休息。

一种可能的解决方案是将这种责任委托给一个新组件并使用像抽象工厂这样的创建设计模式,它将提供一个接口来实例化组件而无需指定其类。

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

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

于 2015-12-23T20:27:05.150 回答
3

除了萨尔瓦多胡安马丁内斯的回答......

为了实现插件架构,Java 的Jar 文件规范提供了对服务提供者接口 ( SPI ) 以及如何查找它们的支持。

从 Java 1.6 开始。您可以使用ServiceLoader来查找服务提供者。对于 Java 1.5。更少你必须自己做或使用图书馆。例如commons-discovery

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

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

 com.a2i.weatherforecastio.ForecastIO

该行必须是IWeather实现类的完整限定名称。

对其他模块执行相同的操作,您可以通过ServiceLoader.

ServiceLoader<IWeather> weatherServicesLoader = ServiceLoader.load(IWeather.class);
Iterator<IWeather> weatherServices = weatherServicesLoader.iterator();

现在它取决于您的运行时类路径将找到多少服务。尝试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

于 2015-12-25T11:01:16.857 回答
0

一种解决方案是您必须使用所有已识别的常见操作定义通用接口。扩展/插件需要实现该接口,并且必须为常见操作提供实现。

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

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

于 2021-09-09T04:00:25.957 回答