4

我正在进行一个大规模的重构项目,代码有一个 5000 行的主类,它被注入到所有内容中,存储了所有内容并拥有所有公共代码。

我不是分析和设计方面的专家,但我已尽我所能将事物分离出来,我大约 80% 是通过重构依赖于主类的类来使用我创建的新类。

有一些类型的数据在应用程序启动时被初始化,并且在应用程序的整个生命周期中几乎所有东西都可以访问它们。例如,有一个包含数百个参数的 Config 类。

我采用的方法是创建几个单例,其中两个最重要的是 GUIData 和 ClientData。GUIData 包含对应用程序主机的引用,clientdata 维护对配置和其他类似类的引用。

这允许我从代码中的任何位置调用 ClientData.getInstance().getConfig().getParam("param") 但我不觉得这是最好的方法。

我考虑了单独的静态类,而不是这些包含类实例的数据单例,但某些类确实需要构造函数。

我一直在谷歌上搜索一个星期,试图找到一种更好的方法来做到这一点,但不知何故,我总是在谈论数据库缓存的线程上结束

4

4 回答 4

3

不可变(配置)实例提供“线程安全的应用程序范围的数据访问”。Typesafe 的配置(正如 Brian Kent 的评论中所建议的)正是这样做的。请注意,这不涉及静态类或单例。静态类和单例现在可以满足您的目的,但将来它们可能会很麻烦。它们当然很方便,但请尝试限制它们的使用。

在读取和解析配置数据后必须进行初始化。它通常在应用程序启动时完成,在其他处理线程启动之前。初始化必须尽可能多地验证配置数据,以便在配置数据不好时快速失败并终止程序。

将大量配置数据捆绑在一起可能会创建“隐藏的通信线路”。例如,您更新一个值并且应用程序失败,因为它还需要更新其他值。将所有配置数据放在一个文件中并从那里加载它是非常好的,但是您的应用程序(具有数百个配置选项)应该将配置数据划分为应用程序不同部分使用的集合。这提高了隔离性,有助于单元测试,并且可以在将来更改应用程序而不会遇到太多令人讨厌的意外。

使用一组配置数据有两种方法:

  1. 从对象内部调用单例Settings.getInstance().getConfigForThisModule()
  2. 通过构造函数或通过为每个使用配置数据的对象提供配置数据setConfig(ConfigForThisModule config)

第一种方法取决于不调用的约定,Settings.getInstance().getConfigForACompletelyUnrelatedModule()这可能是一个弱点。第二种方法更符合“依赖注入”,并且可能更适合未来。您可以在重构时混合使用这两种方法,只要确保一致(例如,仅对应用程序所有部分中使用的配置数据使用单例方法)。

要进一步改进使用配置数据的设计,请牢记以下(可能的)未来功能要求:更新配置文件时,会重新加载配置数据并在应用程序中使用。大多数日志框架都设法支持此功能要求,而不会影响多线程应用程序的性能。除其他外,它需要您的应用程序的以下内容:

  • 如果新的配置数据不好,程序不会终止,而是记录一个错误,旧的配置数据仍在使用中。您的初始化过程将需要处理“重新启动时加载”和“重新加载”两种情况。主要需要注意的是,您的初始化过程需要可重用,并且不应影响应用程序的其他(运行)部分(再次隔离)。
  • 长生命周期的对象可能不会保留配置数据的本地副本或对 的实例的引用ConfigForThisModule,而是Settings.getInstance()...应该定期调用(或其他可以返回更新实例的方法)。
  • 用新配置替换旧配置可能不会导致错误。从技术上讲,替换配置就像AtomicReference使用返回的新配置实例更新Settings.getInstance().... 但这也是测试配置数据集隔离的地方:在一个模块中使用旧集并在另一个模块中同时使用新集应该没有问题。

配置数据可以看作是一种“全局状态”。考虑到这一点,在以下两个问题中讨论了关于做什么和避免什么的进一步设计点(部分公然复制到这个答案):

于 2016-05-25T23:51:02.750 回答
0

如果它只是您需要的 String 属性值,那么您甚至不需要一个类 - 已经为您提供了一个全局设施:System.getProperties()

您需要做的就是首先在启动时加载属性值:

System.setProperty("myKey", "myValue"); // see below how load properties from a file

然后在代码中的任何位置阅读它:

String myValue = System.getProperty("myKey");

或者

String myValue = System.getProperty("myKey", "my desired default");

如果您的容器不支持开箱即用的属性加载,请从如下所示的外部文件加载属性:

key1=value
key2=some other value
etc...

您可以使用以下代码:

Files.lines(Paths.get("path/to/file"))
  .filter(line -> !line.startsWith("#") || !line.contains("=")) // ignore comment/blank
  .map(line -> line.split("=", 2)) // split into key/value
  .forEach(split -> System.setProperty(split[0], split[1])); // load as property
于 2016-05-24T23:28:18.900 回答
0

抱歉,这个问题有点含糊,您是要存储配置或程序其他部分使用的缓存对象吗?

由于您有 100 个参数,因此首先将配置拆分为可管理的块

1) 将您的配置参数拆分为与简单属性文件 1:1 对应的逻辑块 - 这需要一些时间

2) 这些属性文件必须外部化,以便您可以随时更改它们,确保您通过 env 变量将基本位置传递给程序

3) 编写一个实用程序类(单例),它包装Apache 公共配置以保存您的配置。(从基本位置读取 *.properties 并将属性合并到一个配置对象中)这必须在启动任何线程之前完成。

4) 使用 config.getXXXX() 方法参考代码中的配置参数

Apache commons config 还可以在文件系统上的属性文件更改时重新加载配置。

完成后,使用像 Spring 或 Guice 这样的 DI 容器来缓存配置的对象。

于 2016-05-24T17:52:52.380 回答
0

您可以使用 Java Properties 类 util,基本上它是一个 HashTable 参考:https ://docs.oracle.com/javase/7/docs/api/java/util/Properties.html

您创建一个文件 fileName.properties 并将数据存储在键值对中,例如:

username=your name
port=8080

然后将其加载到 Properties Object 并获取如下数据:

Properties prop = new Properties();
load the file...
String userName = prop.getProperty("username")
String port = prop.getProperty("port")// you can parse it to int if needed

我建议为每种类型的配置创建一个属性文件,例如:

  1. clientData.properties
  2. appConfig.properties

你可以按照这个简单的教程 http://www.mkyong.com/java/java-properties-file-examples/

于 2016-05-25T08:09:10.217 回答