如何设计一个具有多种配置选项的程序?

12 投票
4 回答
2823 浏览
提问于 2025-04-17 01:38

假设我有一个程序,它有很多配置选项。用户可以在一个配置文件里指定这些选项。我的程序可以解析这个配置文件,但我该如何在内部存储和传递这些选项呢?

在我的情况下,这个软件是用来进行科学模拟的。大约有200个选项,其中大多数都有合理的默认值。通常情况下,用户只需要指定十几个选项。我面临的困难是如何设计我的内部代码。许多需要构造的对象依赖于很多配置选项。例如,一个对象可能需要几个路径(用于存储数据的地方),一些需要传递给它调用的算法的选项,还有一些是对象本身直接使用的选项。

这就导致了对象在构造时需要传入非常多的参数。此外,由于我的代码库正在积极开发中,逐层传递一个新的配置选项到需要它的地方是非常麻烦的。

一种避免这种麻烦的方法是使用一个全局配置对象,可以在代码的任何地方自由使用。但我并不特别喜欢这种方法,因为这会导致函数和类不接受任何参数(或者只接受一个参数),读者不容易理解这个函数或类处理的数据是什么。这也阻碍了代码的重用,因为所有代码都依赖于一个巨大的配置对象。

有没有人能给我一些关于这个程序应该如何结构化的建议?

下面是我所说的配置选项传递风格的一个例子:

class A:
    def __init__(self, opt_a, opt_b,  ..., opt_z):
        self.opt_a = opt_a
        self.opt_b = opt_b
        ...
        self.opt_z = opt_z

    def foo(self, arg):
        algo(arg, opt_a, opt_e)

这是全局配置风格的一个例子:

class A:
    def __init__(self, config):
        self.config = config

    def foo(self, arg):
        algo(arg, config)

这些例子是用Python写的,但我的问题适用于任何类似的编程语言。

4 个回答

2

你可以传递一个配置解析的类,或者写一个类来包装它,这样就能智能地提取你需要的选项。

Python的标准库里有一个叫configparser的东西,它可以让你使用一种叫做映射协议的方式来访问INI格式配置文件的各个部分和选项。这样,你就可以像使用字典一样直接获取你的选项。

myconf = configparser.ConfigParser()
myconf.read('myconf.ini')
what_to_do = myconf['section']['option']

如果你想通过属性的方式明确提供选项,可以创建一个类,重写__getattr__方法:

class MyConf:
    def __init__(self, path):
        self._parser = configparser.ConfigParser()
        self._parser.read('myconf.ini')
    def __getattr__(self, option):
        return self._parser[{'what_to_do': 'section'}[option]][option]

myconf = MyConf()
what_to_do = myconf.what_to_do
4

有几个设计模式可以帮助你

  • 原型模式
  • 工厂模式和抽象工厂模式

可以把这两种模式用在配置对象上。每个方法会接收一个配置对象,然后根据需要使用里面的内容。同时,考虑把配置参数进行逻辑分组,想想有没有办法减少输入的数量。

伪代码

// Consider we  can run three different kinds of Simulations. sim1, sim2, sim3

ConfigFactory configFactory = new ConfigFactory("/path/to/option/file");
....
Simulation1 sim1;
Simulation2 sim2;
Simulation3 sim3;

sim1.run( configFactory.ConfigForSim1() );
sim2.run( configFactory.ConfigForSim2() );
sim3.run( configFactory.ConfigForSim3() );

在每个工厂方法内部,可以从一个原型对象创建配置,这个原型对象包含了所有“合理”的默认值,而选项文件只包含那些与默认值不同的部分。这样做的同时,应该有清晰的文档说明这些默认值是什么,以及在什么情况下人(或其他程序)可能想要更改它们。

** 编辑: ** 还要考虑每个工厂返回的配置是整体配置的一个子集。

5

matplotlib是一个功能强大的库,里面有很多配置选项。它使用一个叫做rcParams的模块来管理所有的默认设置。rcParams会把所有的默认参数保存在一个字典里。

每个函数都会从关键字参数中获取这些选项:

比如:

def f(x,y,opt_a=None, opt_b=None):
    if opt_a is None: opt_a = rcParams['group1.opt_a']

撰写回答