从全局变量到IOC模式


(2008-02-20 18:06:23)

Java怎么生成全局序列号_lua转载


很早以前,在我初学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 *L = 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++公民。著名的MeyerSingleton像这样
List 5

class Status
 {
 private:
    Status(){};
 public:
 static Status& getInstance()
     {
 static Status s;
 return s;
     }
 };
 class User
 {
 void fun()
     {
         Status&s =Status::Instance();
 //. . .use s
     }
 };

一切似乎很完美,使用private来防止client创建它,保证了对象的唯一性(注意:Meyersingleton并不具有多线程安全,可能导致多次初始化对象)
但是随着针对接口编程和单元测试越来越流行,singleton带来的对单元测试不友好的特点日益体现,全局变量不能很好的被mock,所以难于测试。
这时候所谓的IOC思想(Inversion ofControl,即反转模式)出来了,简单的来说,就是通过构造函数或者set方法实现注入
List6 - 构造函数注入

class Status{};
 class User
 {
 public:
       User(Status*s):m_ps(s){};
 void fun()
       {
            Status*s = m_ps;
       }
 private:
       Status *m_ps;
 }List7 - Set 注入
class Status{};
 class User
 {
 public:
      User(){}
 void setStaus(Status *s)
       {
            m_ps =s;
       }
 void fun()
       {
            Status*s = 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">
 <propertyname="status"><refbean="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,那你就没有办法把代码写的很简单,因为你要考虑的东西多了,可维护性,可测试性。