C++应用程序/程序设置类?
将 Visual Studio C++ 与 MFC 结合使用。我试图找出存储应用程序/程序设置的好方法。我不是指它们的持久存储,而是指代码中用于保存设置的数据结构。
我创建了一个名为 Settings 的静态类,它具有多个静态方法以及用于对设置进行分区的嵌套类。例如:
class Settings
{
public:
Settings(void);
~Settings(void);
static void SetConfigFile(const char * path);
static CString GetConfigFilePath();
static void Load();
static void Save();
class General
{
public:
static CString GetHomePage();
static void SetHomePage(const char * url);
private:
static int homePageUrl_;
};
private:
static CString configFilePath_;
};
然后我可以在整个代码中访问我的设置,例如:
Settings::General::GetHomePage();
现在我正在进入单元测试,并且我开始意识到静态类是不可取的。所以我想把它变成一个基于实例的类。但我必须管理嵌套类实例,这很简单,但对于测试来说仍然有点麻烦。嵌套类的全部目的只是将设置分组为逻辑组。我正在争论基于字符串的设置类是否会更好,例如 settings->get("General.HomePage") 尽管我认为我更喜欢专用访问器方法的强类型。
那么,为了回答我的问题,什么是保存支持简单单元测试的程序配置/设置的良好数据结构?
Using Visual Studio C++ with MFC. I'm trying to figure out what would be a good way to store application/program settings. I'm not referring to their persistent storage but to the data structure used in code to hold the settings.
I created a static class called Settings that has several static methods and also nested classes to partition the settings. For example:
class Settings
{
public:
Settings(void);
~Settings(void);
static void SetConfigFile(const char * path);
static CString GetConfigFilePath();
static void Load();
static void Save();
class General
{
public:
static CString GetHomePage();
static void SetHomePage(const char * url);
private:
static int homePageUrl_;
};
private:
static CString configFilePath_;
};
Then I can access my settings throughout my code like:
Settings::General::GetHomePage();
Now I'm getting into unit testing and I'm starting to realize that static classes are undesirable. So I want to turn this into an instance based class. But I'll have to manage the nested class instances which is trivial but still seems a little cumbersome for testing. The whole intention of the nested classes is simply to group the settings into logical groups. I'm debating whether a string-based settings class would be better, something like settings->get("General.HomePage") although I think I prefer the strong typing of dedicated accessor methods.
So to get to my question what is a good data structure to hold program configuration/settings that supports straightforward unit testing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果这对您有用,您可以这样做。您可以放弃枚举并使用 const 字符串甚至自由格式字符串。枚举实际上也不必在类中定义。有很多方法可以做到这一点。
如果您想实现类别,另一个类可以使用模板来定义枚举类型,对多个实例执行类似的操作。
只是一个想法。
You can do this if it works for you. You can ditch the enum and go to const strings or even free-form strings. The enum doesn't really have to be defined in the class either. There are lots of ways to do it.
Another class could do something similar with multiple instances using a template to define the enum type if you wanted to implement categories.
Just an idea.
我认为您的要求之间不一定存在冲突:(1)提供对配置变量的类型安全访问; (2) 使用
“complete.scoped.name”
语法来指定配置变量的名称。当然,您可以进行类型安全的操作,例如:您可能会通过阅读《入门指南》的第 2 章和第 3 章找到一些灵感 (PDF, HTML) 为我的 Config4Cpp 库。
编辑:我提到的 Config4Cpp 文档可能会为 API 设计提供灵感,但我迟来的意识到,如果您决定从头开始编写自己的配置类(而不是使用第三方库(例如 Config4Cpp)...
您的类应该使用
std::map
来存储 completelyScopedName->value 映射的集合。显然,fullScopedName 将是一个字符串,但有两个选项可用于表示值。第一个选项是将值表示为字符串。类型安全的访问器(例如
getInt()
或getBool()
)将从映射中检索基于字符串的值,然后解析它以将其转换为所需的类型。如果解析失败,则访问器操作将引发异常。 (这是 Config4Cpp 所采用的方法。)第二个选项是表示值,如下面的伪代码所示:
然后可以将类型安全访问器的实现编码如下(伪代码):
I don't think there has to be a conflict between your requirements of: (1) providing type-safe access to configuration variables; and (2) using a
"fully.scoped.name"
syntax to specify the name of a configuration variable. Surely you could have type-safe operations such as:You might find some inspiration by reading Chapters 2 and 3 of the Getting Started Guide (PDF, HTML) for my Config4Cpp library.
Edit: The Config4Cpp documentation I mentioned might provide inspiration for API design, but I belated realised that you might appreciate advice on implementation options in case you decide to write your own configuration class from scratch (rather than use a third-party library like Config4Cpp) ...
Your class should use a
std::map
to store a collection of fullyScopedName->value mappings. Obviously, the fullyScopedName will be a string, but there are two options for representing the value.The first option is to represent the value as a string. A type-safe accessor such as
getInt()
orgetBool()
will retrieve the string-based value from the map and then parse it to convert it into the desired type. If the parsing fails, then the accessor operation throws an exception. (That is the approach taken by Config4Cpp.)The second option is to represent value as shown in the pseudocode below:
The implementation of a type-safe accessor can then be coded as follows (pseudocode):
这是我现在使用的类,主要受到 Nathan 答案的启发,除了模板化方法之外:
This is the class that I'm using now mostly inspired by Nathan's answer except with templated methods: