很早以前,在我初學(xué)c語(yǔ)言的時(shí)候,我的第一個(gè)象樣的程序是一個(gè)五子棋程序,使用了TC2.0的圖形庫(kù),純面向過(guò)程的設(shè)計(jì),由上到下的設(shè)計(jì),而且只有一個(gè)c文件就搞定了,大概幾百行,可惜代碼已經(jīng)失傳,非常可惜。
為什么要全局變量?
List 1
int main()
{
int s1,s2,s3;
fun1(s1);
fun2(s1,s2);
fun3(s1,s2,s3);
return 0;
}
上面的s1,s2,s3如果改成全局變量,則變?yōu)?BR>List 2
int s1,s2,s3;
int main()
{
fun1();
fun2();
fun3();
}
似乎簡(jiǎn)潔了一些,而且沒(méi)有了傳遞參數(shù)的開(kāi)銷(xiāo)。但是缺點(diǎn)也是很明顯的,帶了三個(gè)函數(shù)之間的耦合。
既然我們認(rèn)識(shí)到全局變量的問(wèn)題,怎么改進(jìn)呢?
代碼1中由于有三個(gè)變量,如果有更多的,就更麻煩,我們可以這樣改進(jìn)
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;
}
請(qǐng)注意到這里的lua_open方法,這其實(shí)是一種創(chuàng)建自己的工廠方法。不使用全局變量的好處就是,我們保留了可以創(chuàng)建多個(gè)對(duì)象的自由。
時(shí)代在發(fā)展,進(jìn)入C++時(shí)代,但是全局變量仍然有人在用,存在就是合理的。GOF提出一種設(shè)計(jì)模式叫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 &s = Status::Instance();
//. . .use s
}
};
一切似乎很完美,使用private來(lái)防止client 創(chuàng)建它,保證了對(duì)象的唯一性(注意:Meyer singleton并不具有多線程安全,可能導(dǎo)致多次初始化對(duì)象)
但是隨著 針對(duì)接口編程和單元測(cè)試越來(lái)越流行,singleton帶來(lái)的對(duì)單元測(cè)試不友好的特點(diǎn)日益體現(xiàn),全局變量不能很好的被mock,所以難于測(cè)試。
這時(shí)候所謂的IOC思想(
Inversion of Control,即反轉(zhuǎn)模式)出來(lái)了,簡(jiǎn)單的來(lái)說(shuō),就是通過(guò)構(gòu)造函數(shù)或者set方法實(shí)現(xiàn)注入
List6 - 構(gòu)造函數(shù)注入
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的好處是帶來(lái)了更強(qiáng)大的靈活性,但是帶來(lái)的問(wèn)題就是調(diào)用者麻煩了(天下沒(méi)有免費(fèi)的午餐阿)
List8
int main()
{
Status s;
User u;
u.setStatus(&s);
u.fun();
return 0;
}
好像一切又返樸歸真,似乎并沒(méi)有帶來(lái)什么簡(jiǎn)單。有的時(shí)候簡(jiǎn)單和靈活性就是死對(duì)頭。
為了簡(jiǎn)化用戶(hù)進(jìn)行手工注入,IOC容器出現(xiàn),在Java世界里面,最著名的莫過(guò)于Spring了.IOC容器就像一個(gè)巨大的創(chuàng)建工廠,她可以使用xml來(lái)配置這些,這真的是一場(chǎng)革命。
<beans>
<bean id="status" class="Status">
</bean>
<bean id="user" class="User">
<property name="status"><ref bean="status"/></property>
</bean>
</beans>
Spring就是這樣把注入的工作移到配置文件中去,提供了強(qiáng)大的靈活性和可配置性
但是由于c/c++ 不具備的java那么多運(yùn)行期的類(lèi)型識(shí)別和反射的功能,所以我目前還沒(méi)有發(fā)現(xiàn)有在C++中使用的IOC容器,如果你知道,請(qǐng)告訴我
那么如果是C++怎么來(lái)使注入變得簡(jiǎn)單一點(diǎn)呢,可以使用工廠方法了
List9
User * createUser(Status &s,Status2 &s2)
{
User *user = new User();
user->setStatus(s);
user->setStatus2(s2);
return user;
}
總結(jié):
其實(shí)軟件的設(shè)計(jì)根本就沒(méi)有所謂的黃金法則,沒(méi)有免費(fèi)的午餐,你在獲得更強(qiáng)大的靈活性,往往都得到復(fù)雜性的附加效果。如果你就是寫(xiě)一個(gè)自己玩的小游戲,ok,你就是用全局變量。如果你要設(shè)計(jì)龐大的Office,那你就沒(méi)有辦法把代碼寫(xiě)的很簡(jiǎn)單,因?yàn)槟阋紤]的東西多了,可維護(hù)性,可測(cè)試性。