如何在 object 结构中最小化样板文件和耦合?

How can I minimize both boilerplate and coupling in object construction?

我有一个 C++20 程序,其中配置通过 JSON 从外部传递。根据“Clean Architecture”,我想尽快将信息转换为 self-defined 结构。 JSON 的用法仅在“外环”中很明显,并没有遍及我的整个程序。所以我想要我自己的 Config 结构。但我不确定如何以一种安全的方式编写构造函数,以防止丢失初始化、避免冗余并将外部库与我的核心实体分开。

分离的一种方法是定义没有构造函数的结构:

struct Config {
  bool flag;
  int number;
};

然后在另一个文件中,我可以编写一个依赖于 JSON 库的工厂函数。

Config make_config(json const &json_config) {
  return {.flag = json_config["flag"], .number = json_config["number"]};
}

这样写还是比较安全的,因为可以直接看到struct字段名是如何对应JSON字段的。我也没有那么多冗余。但是我真的没有注意到字段是否未初始化。

另一种方法是使用显式构造函数。如果我忘记初始化字段,Clang-tidy 会警告我:

struct Config {
  Config(bool const flag, int const number) : flag(flag), number(number) {}

  bool flag;
  int number;
};

然后工厂将使用构造函数:

Config make_config(json const &json_config) {
  return Config(json_config["flag"], json_config["number"]);
}

我现在只需指定字段名称五次。而在工厂函数中,对应关系并不清晰可见。 IDE肯定会显示参数提示,但感觉很脆弱。

一种非常紧凑的编写方式是使用一个采用 JSON 的构造函数,如下所示:

struct Config {
  Config(json const &json_config)
      : flag(json_config["flag"]), number(json_config["number"]) {}

  bool flag;
  int number;
};

真的很短,会警告我未初始化的字段,字段与 JSON 之间的对应关系是直接可见的。但是我需要在我的 Config.h 文件中导入 JSON header,我真的很不喜欢。这也意味着如果我要更改配置的加载方式,我需要重新编译使用 Config class 的所有内容。

当然,C++ 是一种需要大量样板代码的语言。从理论上讲,我最喜欢第二种变体。它是最封闭的,最分离的。但是编写和维护是最糟糕的。考虑到实际代码中字段的数量要大得多,我会牺牲编译时间来减少冗余和提高可维护性。

是否有其他组织方式,或者最分离的变体也是样板代码最多的变体?

我会采用构造方法,但是:

// header, possibly config.h

// only pre-declare!
class json;

struct Config
{
    Config(json const& json_config); // only declare!

    bool flag;
    int number;
};

// now have a separate source file config.cpp:

#include "config.h"
#include <json.h>

Config::Config(json const& json_config)
    : flag(json_config["flag"]), number(json_config["number"])
{ }

简洁的方法,您可以避免 json header 的间接包含。当然,构造函数被复制为声明和定义,但这是通常的 C++ 方式。