小明思考

高性能服务器端计算
posts - 70, comments - 428, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

从全局变量到IOC模式

Posted on 2006-01-20 17:23 小明 阅读(4920) 评论(7)  编辑 收藏 引用 所属分类: C/C++
很早以前,在我初学c语言的时候,我的第一个象样的程序是一个五子棋程序,使用了TC2.0的图形库,纯面向过程的设计,由上到下的设计,而且只有一个c文件就搞定了,大概几百行,可惜代码已经失传,非常可惜。

为什么要全局变量?
List 1
int main()
{
int s1,s2,s3;
fun1(s1);
fun2(s1,s2);
fun3(s1,s2,s3);
return 0;
}
上面的s1,s2,s3如果改成全局变量,则变为
List 2
int s1,s2,s3;

int main()
{
    fun1();
    fun2();
    fun3();
}
似乎简洁了一些,而且没有了传递参数的开销。但是缺点也是很明显的,带了三个函数之间的耦合。

既然我们认识到全局变量的问题,怎么改进呢?
代码1中由于有三个变量,如果有更多的,就更麻烦,我们可以这样改进
List 3
typedef struct statusTag
{
    
int s1,s2,s3;
}Status;

int main()
{
    Status s;
    fun1(
&s);
    fun2(
&s);
    fun3(
&s);
    
return 0;
}
这种技巧你可以在lua中看到,看lua的使用代码
List4
#include "lua.h"
#include 
"lauxlib.h"
#include 
"lualib.h"

int main(int argc, char *argv[])
{
    lua_State 
*= lua_open();
    
const char *buf = "var = 100";
    
int var ;
    luaopen_base(L);
    luaopen_io(L);
    lua_dostring(L, buf);
    lua_getglobal(L, 
"var");
    var 
= lua_tonumber(L, -1);
    lua_close(L);
    
    
return 0;
请注意到这里的lua_open方法,这其实是一种创建自己的工厂方法。不使用全局变量的好处就是,我们保留了可以创建多个对象的自由。

时代在发展,进入C++时代,但是全局变量仍然有人在用,存在就是合理的。GOF提出一种设计模式叫Singleton的模式,其核心思想就是不让全局变量漂浮在空中,把它放入class中,成为衣冠楚楚的C++公民。著名的Meyer Singleton像这样
List 5
class Status
{
private:
    Status(){};
public:
    
static Status& getInstance()
    {
           
static Status s;
           
return s;
    }
};

class User
{
    
void fun()
    {
        Status 
&= Status::Instance();
        
//. . .use s
    }
};

一切似乎很完美,使用private来防止client 创建它,保证了对象的唯一性(注意:Meyer singleton并不具有多线程安全,可能导致多次初始化对象)

但是随着 针对接口编程和单元测试越来越流行,singleton带来的对单元测试不友好的特点日益体现,全局变量不能很好的被mock,所以难于测试。

这时候所谓的IOC思想(Inversion of Control,即反转模式)出来了,简单的来说,就是通过构造函数或者set方法实现注入

List6 - 构造函数注入
class Status{};

class User
{
   
public:
      User(Status 
*s):m_ps(s){};
      
void fun()
      {
           Status 
*= m_ps;
      }
   
private:
      Status 
*m_ps;
}

List7 - Set 注入
class Status{};

class User
{
   
public:
      User(){}
      
      
void setStaus(Status *s)
      {
           m_ps 
= s;
      }
      
      
void fun()
      {
           Status 
*= m_ps;
      }
   
private:
      Status 
*m_ps;
}

使用IOC的好处是带来了更强大的灵活性,但是带来的问题就是调用者麻烦了(天下没有免费的午餐阿)

List8
int main()
{
   Status s;
   User u;
   u.setStatus(
&s);
   u.fun();
   
return 0;
}

好像一切又返朴归真,似乎并没有带来什么简单。有的时候简单和灵活性就是死对头。

为了简化用户进行手工注入,IOC容器出现,在Java世界里面,最著名的莫过于Spring了.IOC容器就像一个巨大的创建工厂,她可以使用xml来配置这些,这真的是一场革命。

<beans>
    
<bean id="status" class="Status">
    
</bean>

    
<bean id="user" class="User">
        
<property name="status"><ref bean="status"/></property>
    
</bean>
</beans>
Spring就是这样把注入的工作移到配置文件中去,提供了强大的灵活性和可配置性


但是由于c/c++ 不具备的java那么多运行期的类型识别和反射的功能,所以我目前还没有发现有在C++中使用的IOC容器,如果你知道,请告诉我

那么如果是C++怎么来使注入变得简单一点呢,可以使用工厂方法了

List9
User * createUser(Status &s,Status2 &s2)
{
   User 
*user = new User();
   user
->setStatus(s);
   user
->setStatus2(s2);
   
return user;
}


总结:
其实软件的设计根本就没有所谓的黄金法则,没有免费的午餐,你在获得更强大的灵活性,往往都得到复杂性的附加效果。如果你就是写一个自己玩的小游戏,ok,你就是用全局变量。如果你要设计庞大的Office,那你就没有办法把代码写的很简单,因为你要考虑的东西多了,可维护性,可测试性。

Feedback

# re: 从全局变量到IOC模式  回复  更多评论   

2006-02-11 16:24 by 3×7=51
文章写得不错,cppblog首页上的大部分文章如果都能象这篇文章这样言之有物就好了。

# re: 从全局变量到IOC模式  回复  更多评论   

2006-03-13 22:02 by fiestay
真的很不错,希望能看到更多精彩文章:)

# re: 从全局变量到IOC模式  回复  更多评论   

2006-05-17 10:12 by Roger
写得不错
不过spring用xml来配置带来了一个不方便的地方,就是没办法进行重构,xml hell.

# re: 从全局变量到IOC模式  回复  更多评论   

2008-01-11 10:40 by thh
http://code.google.com/p/pococapsule/

c++ 的ioc,我也是刚刚看到

# re: 从全局变量到IOC模式  回复  更多评论   

2009-02-13 16:20 by aztack
好文

# re: 从全局变量到IOC模式  回复  更多评论   

2009-03-31 11:10 by 1234
看上去有点意思,但实际上完全没讲到点子上

依赖注入、控制反转~
什么是依赖?谁依赖谁?如何注入?
什么是控制?谁控制谁?为何称为反转?

请找经典文章仔细研究

# re: 从全局变量到IOC模式  回复  更多评论   

2009-09-17 14:37 by tom zhou
PocoCapsule/C++ IoC and DSM Framework

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理