唬人的Ioc和DI

初次接触Spring时,Ioc和DI迎面而来,控制反转、依赖注入,之前从没有听过,看解释也没看懂,顿时感到深深的恐惧,c++程序已经落后时代很久了么!

所幸看不懂概念,照猫画虎也能用这些机制,并且用的毫无困难,全然忘记了自己不理解Ioc和DI,甚至觉得没有这两个概念更好。倒不妨先看看Spring中的DI解决了什么问题!

1 从全局变量到单例

和c++不同,java 没有全局变量的概念,那需要用全局变量时怎么办?比如,启动时加载的配置。答案是单例。因为每次创建一个单例的对象得到的都是同一个,所以完全胜任全局变量的工作,并且不得不说,更优雅。反省之前的代码,虽然极力避免全局变量,但毕竟不能彻底避免,如果用单例,可能看起来,代码效果要好很多。

2 工厂模式

我几乎没用过工厂模式,觉得这玩意不实用。直到有天,我发现jackson的 ObjectMapper 有很多的配置选项,如果每次new出来一个对象,都做很多的配置,就太闹心了。这时候工厂模式就派上用场了,通过配置工厂,每次得到配置好的 ObjectMapper 。这里的工厂一般是个单例,不然又回到了每次都配置工厂的老路上。

3 用工厂模式隐藏对象的细节

假设我们实现了一个字符串排序的程序,我们希望能够配置,默认按照字典序排序,当字符串都是数字是,希望按照大小排序。直观看,程序应该这么写,如果默认配置,调用字符串排序函数,如果数字排序配置,调用数字排序函数。如果增加了别的排序函数,继续增加条件分支。

另一种思路是,使用工厂模式,工厂根据配置生成相应的排序对象,程序自身不需要关心具体怎么排序( 子类怎么实现 ),它只需要排序( 父类或者接口定义 )。如果是java这种支持反射的语言,可以直接配置类名,如果是c++,还得根据 if-else 或者 预配置的表 来创建相应对象。

这里对象的所有细节都被隐藏了,程序创建的只是具有 某种行为 的对象,并不关心 某种行为具体怎么做的 ,这就是多态。

4 Ioc 控制反转

对象一般是这么创建的 Order *order = new IntOrder ,在 创建对象的时候 ,就 知道 要创建什么对象,使用工厂是这么创建的 Order *order = OrderFactory::getInstance()创建对象的时候不知道 创建的是什么对象,我们把它叫做 控制反转 ,为这点事儿还起个名字,真是多余。

5 反射的影响

java支持反射,这深刻的影响了java的很多特性。在java中甚至不需要使用工厂,而是通过反射来发现对象。完全可以在构造函数中,通过反射发现某个class的定义,子类,或者某个interface的实现,根据规则生成它们的实例。

6 DI 依赖注入

上面说可以自动找到用来初始化的类,这个过程可以做成通用的实现,这就是 依赖注入对象 不负责自己的初始化,而是由某种机制,找到相应的 ,由该机制初始化它。

7 缺乏反射的语言

反射从本质上说是 自动发现 ,缺乏反射的语言,可以 手动发现 ,可以把需要通过反射发现的东西,手动写入一张表,根据这张表来 注入 。这个过程并没有特别的优势,所以c++程序员对使用Ioc和DI没有特别的动力。

8 本质还是多态

如果一个class只有一个子类,或者一个接口只有一个实现( 没有表现出多态 ),对象的初始化 没有第二个选择 ,既然没有选择,Ioc 和 DI 也没有使用的意义,当然可以假扩展性之名使用。但是到需要扩展的时候再改,并不需要多余的工作,反而是过度设计影响代码质量。

所以本质还是 多态 场景下如何优雅的选择哪个实现的问题。

9 一段c++代码

通过环境变量控制工厂生成的排序类,需要c++11编译。

$ ./order  1 13 2
1 13 2 
$ ORDER_CONFIG=int ./order  1 13 2
1 2 13
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;

class Order {
public:
  virtual vector<string> &perform(vector<string> &v) = 0;
};

class IntOrder : public Order {
public:
  vector<string> &perform(vector<string> &v) {
    sort(v.begin(), v.end(), [](const string &a, const string &b) -> bool {
        return stoi(a) < stoi(b);
      });
    return v;
  }
};

class StringOrder : public Order {
public:
  vector<string> &perform(vector<string> &v) {
    sort(v.begin(), v.end());
    return v;
  }
};

class OrderFactory {
public:
  static Order *getInstance() {
    const char *config = getenv("ORDER_CONFIG");
    if (config && strcmp(config, "int") == 0) {
      return new IntOrder;
    } else {
      return new StringOrder;
    }
  }
};

class Main {
public:
  Main(int argc, char *argv[]) : argc_(argc), argv_(argv) {
    order_ = OrderFactory::getInstance();
  }
  void run() {
    vector<string> v(argv_+1, argv_+argc_);
    order_->perform(v);
    copy(v.begin(), v.end(), ostream_iterator<string>(cout, " "));
  }
private:
  Order  *order_;
  int     argc_;
  char  **argv_;
};

int main(int argc, char *argv[])
{
  Main main(argc, argv);
  main.run();
  return 0;
}

Author: zzyongx

Created: 2016-01-22 五 10:54

Emacs 24.4.1 (Org mode 8.2.10)

Validate