StupidBeauty
Read times:2620Posted at: - no title specified

Fruit教程翻译: 2. 简单系统, 2. Simple system

这是教程的第二部分, 我们会实现一个简单的命令行工具, 它读入一个数字, 将该数字加1,然后输出结果。 在实际的系统中,如此简单的功能 ,一般会写在单个类中 (甚至直接 写在 main() 函数中 ) 而在此教程中, 我们 (过度)使用依赖注入技术 来将各个功能进行 尽可能细致 地划分, 以向妳展示, Fruit能够帮助 妳利用多个组件构建 出一个复杂系统,同时限制各个组件之间 的依赖关系。

在下面的示例代码中,我们省略掉了那些不相关的代码 (例如,include保护代码 ,以及系统头文件的 include代码) 。完整 的源代码在这里: examples/simple_injection

首先,编写一个接口,表示数字加1:

// incrementer.h

class Incrementer {
public:
  // 返回x + 1
  virtual int increment(int x) = 0;
};

数字加1,其实是加法的一个特例,因此,让我们也编写一个接口,表示加法:

// adder.h

class Adder {
public:
  // 返回 x y 的和。
  virtual int add(int x, int y) = 0;
};

现在,我们想要这样一个组件,当向它提供一个Adder 实现的时候,它会提供一个Incrementer 实现。

// incrementer_impl.h

#include "incrementer.h"
#include "adder.h"

fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent();

// incrementer_impl.cpp

#include "incrementer_impl.h"

class IncrementerImpl : public Incrementer {
private:
  Adder* adder;

public:
  INJECT(IncrementerImpl(Adder* adder))
    : adder(adder) {
  }

  virtual int increment(int x) override {
    return adder->add(x, 1);
  }
};

fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent() {
  return fruit::createComponent()
    .bind<Incrementer, IncrementerImpl>();
}

这是我们第一次面对那种本身 有要求的组件。 Fruit组件 是可以有类型要求的, 这些类型,以 fruit::Required<T1, ..., Tn> 的形式在该组件的第一个类型参数中指定。如果 getIncrementerImplComponent() 的原型中未指定要求,那么, Fruit 会寻找针对Adder 的绑定,并且 ,由于未能找到而终止编译 ,报告错误。对于每个组件 ,它尚未绑定的所有必需的类型,都必须被暴露。

注意 头文件中暴露的唯一信息就是, 该模块要求什么、提供什么。 IncrementerImpl 类,只在 .cpp文件 中定义。 这与 透明指针 类似。

注意:习惯了C++风格的读者,可能希望用一个匿名的命名空间来将这个类包装起来。这是一个合理的预期,因为这个类仅仅在该.cpp文件中使用。出于易读性考虑,我们省略掉了这个匿名的命名空间。

同时,这里也缺少了虚析构函数,这也是有意为之,因为,Fruit会在注入器中处理各个对象的析构过程,它会自动销毁那些实体(concrete)类(不是利用指向基类的指针来实现的)。即使妳打开了"-W -Wall"选项,编译器也不会报告警告。

现在,让我们来实现Adder。

// simple_adder.h

#include "adder.h"

fruit::Component<Adder> getSimpleAdderComponent();

// simple_adder.cpp

#include "simple_adder.h"

class SimpleAdder : public Adder {
public:
  INJECT(SimpleAdder()) = default;

  virtual int add(int x, int y) override {
    return x + y;
  }
};

fruit::Component<Adder> getSimpleAdderComponent() {
  return fruit::createComponent()
    .bind<Adder, SimpleAdder>();
}

这个组件狠简单,它的代码已经自解释了。

现在, 我们拥有了一个能够提供 Adder 的组件, 以及一个需要 Adder 而又提供了 Incrementer 的组件。 ,现在,我们要将这两个组件复合起来。

// simple_incrementer.h

#include "incrementer.h"

fruit::Component<Incrementer> getSimpleIncrementerComponent();

// simple_incrementer.cpp

#include "simple_incrementer.h"

#include "incrementer_impl.h"
#include "simple_adder.h"

fruit::Component<Incrementer> getSimpleIncrementerComponent() {
  return fruit::createComponent()
    .install(getIncrementerImplComponent())
    .install(getSimpleAdderComponent());
}

install() 是一个操作函数,它将 一个子组件“安装”到当前组件中去。 我们在上一页教程中已经观摩过相应的处理过程了,在这里, 我们并不将所有能够暴露的接口全部都暴露出去。 Adder接口, 被认为是一个与实现相关的细节,因此 ,我们并不暴露它。注意 ,它甚至并没有被包含到头文件中,仅仅 .cpp文件依赖 ( 通过 simple_adder.h 间接地依赖 ) 这个例子展示了, Fruit能够帮助 妳减少 大型项目中 include指令 的个数 (因而缩短 了编译时间 ) 如果不使用依赖注入,那么, 为了暴露出Incrementer 的实现, 我们需要包含 IncrementerImpl 的头文件, IncrementerImpl实现代码 中又需要包含 SimpleAdder 的头文件。如果使用依赖注入 ,但不使用 Fruit ,那么, IncrementerImpl 的实现代码中不需要包含 SimpleAdder 的头文件,但是,客户代码中还是需要 同时 包含IncrementerImpl 和SimpleAdder 的头文件。

啊!数字 加1,实现完毕。

现在,实现部分
也编写完毕,我们只需要编写 main() 函数了。

#include "simple_incrementer.h"

using fruit::Component;
using fruit::Injector;

int main() {
  Injector<Incrementer> injector(getSimpleIncrementerComponent());
  Incrementer* incrementer = injector.get<Incrementer*>();

  int x;
  std::cin >> x;
  std::cout << incrementer->increment(x) << std::endl;

  return 0;
}

我们利用该组件构造了一个Injector,然后,通过该注入器获取了一个Incrementer 实例。这里,不需要包含任何的类定义头文件。

在以高昂的价格(或者开源, 这取决于妳的喜好 )发布了该程序之后, 我们收到了用户的一些反馈。部分用户 用得狠开心,但是 ,也有一部分用户注意到了一个问题, 对某个巨大的数字加1之后,会产生一个负数。 它们希望我 们加入一个 --checked 选项,用于启用溢出检 查。

我们的实现代码是模块化的(感谢依赖注入),因此,我们不需要对以上的任何组件进行修改。同时,Incrementer组件将具体的工作委托给了Adder,因此,我们此刻唯一要做的就是,对于Adder 写出一种会检查溢出情况的实现。

// checked_adder.h

#include "adder.h"

fruit::Component<Adder> getCheckedAdderComponent();

// checked_adder.cpp

#include "checked_adder.h"

class CheckedAdder : public Adder {
private:
  bool add_overflows(int x, int y) {

... // 具体 的实现代码

}

public:
  INJECT(CheckedAdder()) = default;

  virtual int add(int x, int y) override {
    if (add_overflows(x, y)) {
      std::cerr << "CheckedAdder: detected overflow during addition of " << x << " and " << y << std::endl;
      abort();
    }
    return x + y;
  }
};

fruit::Component<Adder> getCheckedAdderComponent() {
  return fruit::createComponent()
    .bind<Adder, CheckedAdder>();
}

并没有什么新鲜东西。现在,我们将这个新的组件与之前写好的IncrementComponent 组合起来。

// checked_incrementer.h

#include "incrementer.h"

fruit::Component<Incrementer> getCheckedIncrementerComponent();

// checked_incrementer.cpp

#include "checked_incrementer.h"

#include "incrementer_impl.h"
#include "checked_adder.h"

fruit::Component<Incrementer> getCheckedIncrementerComponent() {
  return fruit::createComponent()
    .install(getIncrementerImplComponent())
    .install(getCheckedAdderComponent());
}

除了对 IncrementerComponent 的复用之外, 没有别的什么需要注意的东西了。

// incrementer_component.h

#include "incrementer.h"

fruit::Component<Incrementer> getIncrementerComponent(bool checked);

// incrementer_component.cpp

#include "incrementer_component.h"

#include "simple_incrementer.h"
#include "checked_incrementer.h"

fruit::Component<Incrementer> getIncrementerComponent(bool checked) {
  if (checked)
    return getCheckedIncrementerComponent();
  else
    return getSimpleIncrementerComponent();
}

get * Component() 仅仅 是一个函数而已,因此, 我们可以让它提供出参数化的组件, 只需要 向这个函数加入参数,并且在函数中使用 if 判断 来引入 多个return语句。

现在,让我们重写main()函数,以接受新的参数,并且使用新的参数化的模块。

// main.cpp

#include "incrementer_component.h"

using fruit::Component;
using fruit::Injector;

// Try 一try:
// echo 5 | ./incrementer
// echo 2147483647 | ./incrementer
// echo 2147483647 | ./incrementer --checked
int main(int argc, const char* argv[]) {

  bool checked = false;

  if (argc == 2 && std::string(argv[1]) == "--checked")
    checked = true;

  Injector<Incrementer> injector(getIncrementerComponent(checked));
  Incrementer* incrementer(injector);

  int x;
  std::cin >> x;
  std::cout << incrementer->increment(x) << std::endl;

  return 0;
}

此处 ,我们展示了另一种利用注入器来获取类实例的语法。 我们不是直接调用注入器的 get ,而是, 将注入器转换成我们想要的那种类型。 这是一种便利实现方法,用来避免 将类型重复两次 ( 与上面的 main函数对比 一下 )

教程 的下一部分 中,我们将学习如何使用有辅助的注入。

恒星

高圆圆

未知美人

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands

 
Recent comments
2017年4月~2019年4月垃圾短信排行榜Posted at:Thu Sep 26 04:51:48 2024
Qt5.7文档翻译:QWebEngineCookieStore类,QWebEngineCookieStore ClassPosted at:Fri Aug 11 06:50:35 2023盲盒kill -9 18289 Grebe.20230517.211749.552.mp4