【发布时间】:2014-11-11 19:13:57
【问题描述】:
假设一个类 Foo 有两个依赖项(Bar 和 Baz),并且在不提供这两个依赖项的情况下构造一个 Foo 是错误的。构造函数注入可以很容易地在编译时保证这样做:
class Foo
{
public:
Foo(const std::shared_ptr<Bar>& bar, const std::shared_ptr<Baz>& baz);
// (don't get hung up on the type of pointer used; it's for example only)
};
但是假设 Foo 也需要两个双打:
class Foo
{
public:
Foo(const std::shared_ptr<Bar>& bar, const std::shared_ptr<Baz>& baz,
double val1, double val2);
};
现在有问题了;调用者很容易意外地转置 val1 和 val2 并创建运行时错误。我们可以添加一个 Params 结构来允许命名初始化并排除这种情况:
class Foo
{
public:
struct Params
{
std::shared_ptr<Bar> bar;
std::shared_ptr<Baz> baz;
double val1;
double val2
};
Foo(const Params& params);
};
// ...
std::shared_ptr<Foo> MakeDefaultFoo()
{
Foo::Params p;
p.bar = std::make_shared<Bar>();
p.baz = std::make_shared<Baz>();
p.val1 = 4.0;
p.val2 = 3.0;
return std::make_shared<Foo>(p);
}
但现在我们遇到的问题是调用者可能会忘记填充 Params 中的某个字段,这要到运行时才能检测到。 struct 初始化语法或初始化列表将不可能忘记一个字段,但是我们又回到了依赖位置!
是否有一些技巧可以使两全其美——编译器强制强制参数按名称而不是位置分配?
【问题讨论】:
-
您可以添加
Params() : val1(0), val2(0) { },这样如果未设置值,它至少具有一些合理的默认值。 -
@cdhowie 当我不得不求助于它时,我实际上更喜欢 insane 默认值,这样如果发生这种情况,代码将很快失败。但最好在编译时失败!
-
或者说真的,也许不是
double有两个不同命名类型的两个变量? -
@dlf 在运行时失败是否可以接受,但在调用
Fooctor 时立即失败? -
@cdhowie 总比没有好(这就是我现在所做的),但我宁愿在编译时失败。如果将来不更改语言,我真的不相信有解决这个问题的方法,但我猜你永远不知道。
标签: c++ initialization constructor-injection