我正在创建一个程序,需要设置一些选项值以及一些图像文件路径,SQLite数据库路径,各种按钮上的文本信息,有关使用哪个数据库的信息(例如SQLite/MySQL)等
到目前为止,我有一个帮助类,其中我将所有内容都设置为静态,并且我从程序中的任何位置访问它,但它变得一团糟,我读到这是一种不好的做法,因为它不遵循面向对象的编程准则.
所以我所做的是使该类成为一个单例,并在我的控制器对象中引用它.要访问我的选项,我现在调用controller.getOptionName而不是直接调用Helper.getOptionName.
我有一种感觉,我正在以错误的方式接近这一点,特别是因为从我所读过的内容来看,如果我的许多对象依赖于单个类(助手类),我就不能将这一切分开.
我不知道我应该做什么,是否有一个"标准"在哪里保留我的所有选择?我考虑过使用XML文件或类似的东西,但我最终还是要从任何地方访问它,所以感觉这会产生同样的问题.
问题:配置蔓延随着时间的推移,程序获得了功能和选项.当他们连接到外部系统和服务(例如数据库,事件代理,云/ Web服务)时,他们还必须为这些服务保留越来越多的配置和凭据.
在运行时存储此信息的传统位置是全局变量和OS环境变量.两个都很糟糕.逻辑上配置数据是"全局环境"或运行应用程序的上下文,但您不能轻易依赖全局变量或环境变量.
另一种传统机制,即配置文件 - 无论是XML,INI,.properties还是其他 - 都可以帮助在运行之间存储配置数据,但不会为程序代码或执行期间组织配置/选项.
您可以通过选择应用程序类的属性来清理一些东西.这是传统的"下一步".不幸的是,它可能需要很多前期"什么地方?" 思维.IMO,超过它的价值.即使你做出正确的选择,它也不会持久.如果你有一个功能相当强大的应用程序,随着时间的推移,选项和设置的数量将变得势不可挡.您将花费大量时间手动编写对象构造函数中的默认值和选项以及其他方法的参数/代码.尝试在类之间完美地划分配置不仅耗费精力,而且可以导致高度相互依赖的类.这么干净的封装!
我经常对这个特定的墙壁猛烈抨击,特别是当针对具有"合理"或"智能"默认值和行为的代码时,允许用户随时覆盖默认值,这提供了一个简单的界面不需要了解应用类和组件的完整相互作用以使用其服务.
解决方案:委托给Config对象我发现的最佳解决方案是将option/config数据封装到自己的指定对象中.描述此模式的一种奇特方式:对于配置,设置和选项数据,请使用委派而不是继承或组合.
如何构建配置映射取决于您正在使用的语言.在许多语言中,构建自己的Config对象会给您一个很好的"外观":
if opts.verbose:
print "..."
Run Code Online (Sandbox Code Playgroud)
我发现这比访问属性的更明确的"getter" opts.get("verbose")或"indexer" opts['verbose']方式更具可读性.但是你通常不必自己创建Config类,这基本上只是一个映射.
● 简单方法 ●使用通用映射:例如,在Python a dict,Perl a %hash,Java a Dictionary或HashMap.更好的是,这些扩展设计用于或特别适合于配置数据.在Python中,例如,我使用stuf和TreeDict进行简单的点访问和其他不错的属性.在Java中,Properties是一个类似的特定配置扩展.例如:
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:
...
Run Code Online (Sandbox Code Playgroud)
这样,您的所有配置和选项数据都集中在一个位置,整齐可访问,并且与其并排使用的所有其他程序,类,实例和函数/方法数据清晰地描述.随着程序的发展,这有助于保持一段时间的清晰度.您可以快速确定"这是核心数据的一部分吗?还是与核心数据处理的上下文相关?"
为了使事情变得更好,如果从配置文件预加载配置数据,请将这些值直接加载或复制到配置对象中.如果从命令行获取参数,请将这些值直接加载或复制到配置对象中.现在,您有一个统一的所有"用户希望我做什么,有什么选项和设置?"的来源.信息.
TL; DR - 90%的应用程序或服务都可以通过简单的配置/选项映射完成.以下所有内容均适用于高级用例.因为这是一个设计/模式问题,所以这就是为什么这种方法不是一次性的,而是延伸到更复杂/复杂的用例.
● 每实例配置 ●您可以拥有多个级别的配置/选项数据.最常见的用途是在类或模块级别设置默认值,然后为每个实例设置可能不同的选项.服务器应用程序可能每个用户都有一个实例,每个用户/实例都需要自己的自定义设置.配置映射在实例创建/初始化时自动或显式复制.
●● 多个配置对象 ●●如果有意义,您可以将配置/选项数据分区为多个配置对象.例如,您可以将用于数据检索的选项与用于数据格式化的选项进行分区.您可以在设计开始时执行此操作,但不需要.您可以从一个单片配置对象开始,然后随着时间的推移重构(通常,当您开始重构底层函数时).显然你不想"疯狂"添加配置对象,但你可以有一些没有增加太多的程序复杂性.如果您对配置对象进行分区,则可以通过单个API代理多个配置"域" - 在内部提供高质量的信息分解,但外观非常简单.
◆ Chain Gang ◆比每个实例复制配置数据更优雅:使用可链接或分层映射(例如在Python中ChainMap),它允许您将一个映射的值"叠加"到另一个映射的值(类似于"写时复制"方案) ,或"联合"和"半透明"文件系统).然后,实例选项直接引用类/默认选项 - 除非明确设置它们,在这种情况下它们是特定于实例的.优点:如果在程序执行期间更改了类/默认/全局设置,则后续实例方法调用将"看到"更改的默认值并使用它们(只要它们未在实例级别被覆盖).
◆◆ 瞬态配置 ◆◆如果您需要"动态"更改配置/选项 - 比如说给定方法调用的范围 - 该方法可以扩展实例选项链接映射.在Python中,这是做什么的ChainMap.new_child().这听起来很复杂,但就方法代码而言,它简直就是昙花一现.仍然只有一个配置对象可供参考,无论它说什么都是选项,请使用它.
◆◆◆ 任意持续时间叠加 ◆◆◆方法调用的时间范围没什么神奇之处.通过适当的设置,可以根据需要暂时覆盖任何级别的配置.例如,如果在程序运行期间有一段时间你想打开调试,日志记录或分析,你可以随时打开和关闭它 - 只针对某些实例,或者同时针对所有实例.这种开胃小猫的 使用需要一个Config稍微超过库存的物体 - ChainMap但不是很多(基本上只是链映射的一个句柄).
令人高兴的是,大多数代码都没有接近需要这些"黑钻石"级别的配置复杂性.但是如果你想要深入三到四个级别,委派给单独的配置对象将以合理的方式将你带到那里,使代码保持干净有序.
| 归档时间: |
|
| 查看次数: |
502 次 |
| 最近记录: |