Fruit教程翻译: 3. 有辅助的注入, 3. Assisted injection
在教程的这个部分,我们会构造 一个组件,它的作用是, 按照某个常数因子 ,将双精度浮点数放大对应的倍数。完整 的源代码在这里: examples/scaling_doubles 。
首先,编写一个Multiplier 组件。
// multiplier.h
class Multiplier {
public:
// 返回
x和y的乘积。
virtual double multiply(double x, double y) = 0;
};
fruit::Component<Multiplier> getMultiplierComponent();
// multiplier.cpp
#include "multiplier.h"
class MultiplierImpl : public Multiplier {
public:
double multiply(double x, double y) override {
return x * y;
}
};
fruit::Component<Multiplier> getMultiplierComponent() {
return fruit::createComponent()
.bind<Multiplier, MultiplierImpl>()
.registerConstructor<MultiplierImpl()>();
}
如本例所示,如果某个接口存在着一个标准实现,那么,我们可以将该接口的定义和get*Component()函数的声明放置在同一个头文件中,以省掉一些繁琐的代码。
注意 , MultiplierImpl 的构造函数 并未 使用 INJECT() 包装起来 。 那是一种用来向Fruit 告知 该使用哪个注入器的便利方式,但是, 在某些情况下,无法使用那种方式,例如, MultiplierImpl可能 是由另一个并未使用Fruit 的项目提供的。 本示例中,我们实际上可以使用那种方式,但是, 我们就是不使用,以展示 另一种(等价的)方式,即,显式地使用构造函数 的原型来调用registerConstructor。
// scaler.h
class Scaler {
public:
virtual double scale(double x) = 0;
};
using ScalerFactory = std::function<std::unique_ptr<Scaler>(double)>;
fruit::Component<ScalerFactory> getScalerComponent();
一开始,妳可能争着要去编写
Component<Scaler>
。但是,
我们并没有单个的
Scaler实现,而且,对于每个
双精度浮点数值(即为因子),都存在一个
Scaler实现
。所以
,我们在组件原型中暴露这一点。
在返回数值类型时,
我们可以直接以数值的形式来返回,但是
,对于接口来说,最好
是返回一个
unique_ptr
,以确保
该对象会被销毁。
Fruit
可以注入任意类,但是
,对于像
std::function<std::unique_ptr<T>(...)>
这种形式的类型,有着特殊的支持,我们狠快会看到。
现在开始
写实现代码。
// scaler.cpp
#include "scaler.h"
#include "multiplier.h"
using fruit::Component;
using fruit::Injector;
using fruit::createComponent;
class ScalerImpl : public Scaler {
private:
Multiplier* multiplier;
double factor;
public:
INJECT(ScalerImpl(ASSISTED(double) factor, Multiplier* multiplier))
: multiplier(multiplier), factor(factor) {
}
double scale(double x) override {
return multiplier->multiply(x, factor);
}
};
Component<ScalerFactory> getScalerComponent() {
return createComponent()
.bind<Scaler, ScalerImpl>()
.install(getMultiplierComponent());
}
此处 ,我们首次见到了 ASSISTED() 宏。 它的作用是, 在一个被 INJECT() 包装的构造函数中,标记 出某些不需要被注入的类型,那些类型 会成为被注入的工厂函数的参数。
注意,此处,我们直接安装了这个乘法器组件,而不是将它声明为需求再编写一个将ScalerImplComponent 和MultiplierComponent 复合起来的组件。这是一个折衷:这样做,会稍微削弱模块化特性,但是,会使代码更简洁(避免写出2个文件)。至于空间哪种实现方式更好呢,没有绝对的答案;这种折衷,必须根据具体的情况来评估。
注意 ,此处绑定操作也做了某些不同的事情: 我们没有将 Scaler绑定 到 ScalerImpl , 而是将一个 std::function<std::unique_ptr<Scaler>(double)> 绑定到一个 std::function<std::unique_ptr<ScalerImpl>(double)> 。
// main.cpp
#include "scaler.h"
using fruit::Injector;
int main() {
Injector<ScalerFactory> injector(getScalerComponent());
ScalerFactory scalerFactory(injector);
std::unique_ptr<Scaler> scaler = scalerFactory(12.1);
std::cout << scaler->scale(3) << std::endl;
return 0;
}
这就 是 main() 函数,现在应该 狠容易理解了。 我们从注入器获取了工厂的一个实例,然后,自己调用 该工厂的方法来获取Scaler 的一个实例。
现在,妳已经知道Fruit 的基本用法了。为了进一步熟悉它呢,建议妳尝试着重写上面展示的系统,或者,编写妳觉得有意思的任何其它系统,再改动一下代码,观摩其效果。
在 教程 的下一部分 中,我们将学习 到,Fruit 是如何报告注入错误的。
何 晴
Your opinionsHxLauncher: Launch Android applications by voice commands