保留选项值的位置,重要文件的路径等

我正在创建一个程序,需要设置一些选项值以及图像文件的一些路径,SQLite数据库的路径,各种按钮上的文本的一些信息,有关使用哪个数据库的信息(例如SQLite / MySQL)等

到目前为止,我有一个帮助类,我把所有东西都设置为静态,我从程序中的任何地方访问它,但它变得一团糟,我读到这是一种不好的做法,因为它不遵循面向对象的编程准则。

所以我所做的是使该类成为一个单例,并在我的控制器对象中引用它。 要访问我的选项,我现在调用controller.getOptionName而不是直接调用Helper.getOptionName。

我有一种感觉,我正在以错误的方式接近这一点,特别是因为从我所读过的内容来看,如果我的许多对象依赖于单个类(助手类),我就不能将这一切分开。

我不知道我应该做什么,是否有一个“标准”在哪里保留我的所有选择? 我考虑过使用XML文件或类似的东西,但我最终还是要从任何地方访问它,所以感觉这会产生同样的问题。

问题:配置蔓延随着时间的推移,程序获得了function和选项。 当他们连接到外部系统和服务(例如数据库,事件代理,云/ Web服务)时,他们还必须为这些服务保留越来越多的配置和凭据。

在运行时存储此信息的传统位置是全局变量和OS环境变量。 两个都很糟糕逻辑上配置数据是“全局环境”或运行应用程序的上下文,但您不能轻易依赖全局变量或环境变量。

另一种传统机制,即配置文件 – 无论是XML,INI,.properties还是其他 – 都可以帮助在运行之间存储配置数据,但无需为程序代码或执行期间组织配置/选项。

您可以通过选择应用程序类的属性来清理一些东西。 这是传统的“下一步”。 不幸的是,它可能需要很多前期“什么地方?” 思维。 IMO,超过它的价值。 即使你做出正确的选择,它也不会持久。 如果你有一个function相当强大的应用程序,随着时间的推移,选项和设置的数量将变得势不可挡。 您将花费大量时间手动编写对象构造函数中的默认值和选项以及其他方法的参数/代码。 尝试在类之间完美地划分配置不仅耗费精力,而且可以导致高度相互依赖的类。 这么干净的封装!

我经常对这个特定的墙壁猛烈抨击,特别是当针对具有“合理”或“智能”默认值和行为的代码时,允许用户随时覆盖默认值,这提供了一个简单的界面,不需要了解应用程序类和组件的完整相互作用以使用其服务。

解决方案:委托给Config对象我发现的最佳解决方案是将option / config数据封装到自己的指定对象中。 描述此模式的一种奇特方式:对于配置,设置和选项数据,请使用委派而不是inheritance或组合 。

如何构建配置映射取决于您正在使用的语言。在许多语言中,构建您自己的Config对象会给您一个很好的“外观”:

 if opts.verbose: print "..." 

我觉得比更明确的“getter” opts.get("verbose")或“indexer” opts['verbose']访问属性的方式更具可读性。 但是你通常不需要创建自己的Config类,它基本上只是一个映射。

简单方法 ●使用通用映射:例如,在Python中使用dict ,在Perl中使用%hash ,在Java中使用DictionaryHashMap 。 更好的是,这些扩展设计用于或特别适合于配置数据。 在Python中,例如,我使用stuf和TreeDict进行简单的点访问和其他不错的属性。 在Java中, Properties是一个类似于configs的特定扩展。 例如:

 from stuf import stuf # stuf has attributes! opts = stuf( show_module=False, # comment explaining what show_module means where=True, # ... truncate=False, # ... everything=False, # ... allvars=False, # ... allkeys=False, # ... yaml=False, # ... on=True, # ... ret=None, # ... ) if opts.truncate: ... 

这样,您的所有配置和选项数据都集中在一个位置,整齐可访问,并且与它并排使用的所有其他程序,类,实例和函数/方法数据清晰地描述。 随着程序的发展,这有助于保持清晰度。 您可以快速确定“这是核心数据的一部分吗?还是与核心数据处理的上下文相关?”

为了使事情变得更好,如果从配置文件预加载配置数据,请将这些值直接加载或复制到配置对象中。 如果从命令行获取参数,请将这些值直接加载或复制到配置对象中。 现在,您有一个统一的所有“用户希望我做什么,有什么选项和设置?”的来源。 信息。


TL; DR – 90%的应用程序或服务都可以通过简单的配置/选项映射完成。 以下所有内容均适用于高级用例。 因为这是一个设计/模式问题,所以这就是为什么这种方法不是一次性的,而是延伸到相继更复杂/复杂的用例。

每实例配置 ●您可以拥有多个级别的配置/选项数据。 最常见的用途是在类或模块级别设置默认值,然后为每个实例设置可能不同的选项。 服务器应用程序可能每个用户都有一个实例,每个用户/实例都需要自己的自定义设置。 配置映射在实例创建/初始化时自动或显式复制。

●● 多个配置对象 ●●如果有意义,您可以将配置/选项数据分区为多个配置对象。 例如,您可以将用于数据检索的选项与用于数据格式化的选项进行分区。 您可以在设计开始时执行此操作,但不需要。 您可以从一个单片配置对象开始,然后随着时间的推移重构(通常,当您开始重构底层函数时)。 显然你不想“疯狂”添加配置对象,但你可以有一些没有增加太多的程序复杂性。 如果您对配置对象进行分区,则可以通过单个API代理多个配置“域” – 在内部为您提供高质量的信息分解,但外观非常简单。

Chain Gang ◆比每个实例复制配置数据更优雅:使用可链接或分层映射(例如在Python, ChainMap ),它允许您“覆盖”一个映射的值与另一个映射的值(类似于“copy-on-write”)方案,或“联盟”和“半透明”文件系统)。 然后,实例选项直接引用类/默认选项 – 除非明确设置它们,在这种情况下它们是特定于实例的。 优点:如果在程序执行期间更改了类/默认/全局设置,则后续实例方法调用将“看到”更改的默认值并使用它们(只要它们未在实例级别被覆盖)。

◆◆ 瞬态配置 ◆◆如果您需要“动态”更改配置/选项 – 比如说给定方法调用的范围 – 该方法可以扩展实例选项链接映射。 在Python中,这就是ChainMap.new_child()所做的。 这听起来很复杂,但就方法代码而言,它简直就是昙花一现。 仍然只有一个配置对象可供参考,无论它说什么都是选项,请使用它。

◆◆◆ 任意持续时间叠加 ◆◆◆方法调用的时间范围没什么神奇之处。 通过适当的设置,可以根据需要暂时覆盖任何级别的配置。 例如,如果在程序运行期间有一段时间你想打开调试,记录或分析,你可以随时打开和关闭它 – 只针对某些实例,或者同时针对所有实例。 这种开胃小猫的使用需要一个稍微超出库存ChainMapConfig对象 – 但不是很多(基本上只是链映射的句柄 )。

令人高兴的是,大多数代码都没有接近需要这些“黑钻石”级别的配置复杂性。 但是如果你想要深入三到四个级别,委派给单独的配置对象将以合理的方式将你带到那里,使代码保持干净有序。

我建议将您的选项放入某种文件(无论是xml还是.properties),特别是如果值可以更改(例如数据库用户名等)。 我还会说你可以按组件分解这些配置文件。 就像你分解你的代码一样,需要数据库信息的组件可能不需要图像路径。 因此,您可以拥有数据库信息,图像路径信息等文件。然后让您的组件加载他们需要的文件。

在特定于Java的情况下,您可以将这些文件放在类路径中,并让组件引用它们。 我喜欢使用.properties文件,因为java有一个很好的类, 属性 ,用于处理它们。

因此,这是一个图像提供程序的一个小例子,它给出了一个BufferedImage,给定文件名。

image.properties

  icon.path=/path/to/my/icons background.path=/path/to/my/backgrounds 

确保此文件位于类路径中。 那么这是我的提供者类

  public class ImageProvider { private static final String PROP_FILE = "image.properties"; private static final String ICON_PATH = "icon.path"; private Properties properties; public ImageProvider() { properties = new Properties(); properties.load(getClass().getClassLoader().getResourceAsStream(PROP_FILE)); } public BufferedImage getIcon(String icon) { return ImageIO.read(properties.getProperty(ICON_PATH) + icon); } } 

配置保存在静态变量中或者每个组件独立访问它们的时间很久以前就结束了。 从那以后,人类发明了IoC和DI。 你拿一个DI库(例如spring),用它来加载所有的配置(从文件,jndi,环境,用户,你的名字),并将它注入到需要它的每个组件。 没有手动资源加载,没有静态配置 – 这只是邪恶