C++里面缺少一些有用的框架比如說AOP和IOC等,AOP框架的實現在前面的博文中已介紹了,現在介紹IOC框架。
IOC即控制反轉,它的思想是由IOC容器來管理對象的生命周期、依賴關系等,從而使得應用程序的配置和依賴性規范與實際的應用程序代碼分開。其中一個特點就是通過文本的配置文件進行應用程序組件間相互關系的配置,而不用重新修改並編譯具體的代碼。IOC不僅僅用來解除對象創建的耦合性,還可以使我們能通過配置去創建我們需要的對象,這種靈活性在我們應用開發過程中是非常有用的。C#和Java中有很多IOC框架,遺憾的是C++中卻鮮有IOC框架,本IOC框架填補了這個遺憾。
IOC框架的實現原理:通過向IOC容器注冊類型信息和一個唯一key,在創建時,根據類型信息和key從容器中創建一個實例。下面具體看實現代碼:
#include <string> #include <map> #include <memory> #include <functional> using namespace std; #include <Any> #include<NonCopyable> class IocContainer : NonCopyable { public: IocContainer(void){} ~IocContainer(void){} template <class T> void RegisterType(string strKey) { typedef T* I; std::function<I()> function = Construct<I, T>::invoke; RegisterType(strKey, function); } template <class I, class T, typename... Ts> void RegisterType(string strKey) { std::function<I* (Ts...)> function = Construct<I*, T, Ts...>::invoke; RegisterType(strKey, function); } template <class I> I* Resolve(string strKey) { if (m_creatorMap.find(strKey) == m_creatorMap.end()) return nullptr; Any resolver = m_creatorMap[strKey]; std::function<I* ()> function = resolver.AnyCast<std::function<I* ()>>(); return function(); } template <class I> std::shared_ptr<I> ResolveShared(string strKey) { auto b = Resolve<I>(strKey); return std::shared_ptr<I>(b); } template <class I, typename... Ts> I* Resolve(string strKey, Ts... Args) { if (m_creatorMap.find(strKey) == m_creatorMap.end()) return nullptr; Any resolver = m_creatorMap[strKey]; std::function<I* (Ts...)> function = resolver.AnyCast<std::function<I* (Ts...)>>(); return function(Args...); } template <class I, typename... Ts> std::shared_ptr<I> ResolveShared(string strKey, Ts... Args) { auto b = Resolve<I, Ts...>(strKey, Args...); return std::shared_ptr<I>(b); } private: template<typename I, typename T, typename... Ts> struct Construct { static I invoke(Ts... Args) { return I(new T(Args...)); } }; void RegisterType(string strKey, Any constructor) { if (m_creatorMap.find(strKey) != m_creatorMap.end()) throw std::logic_exception("this key has already exist!"); m_creatorMap.insert(make_pair(strKey, constructor)); } private: unordered_map<string, Any> m_creatorMap; };
使用說明:
類型注冊分成三種方式注冊,一種是簡單方式注冊,它只需要具體類型信息和key,類型的構造函數中沒有參數,從容器中取也只需要類型和key;另外一種簡單注冊方式需要接口類型和具體類型,返回實例時,可以通過接口類型和key來得到具體對象;第三種是構造函數中帶參數的類型注冊,需要接口類型、key和參數類型,獲取對象時需要接口類型、key和參數。返回的實例可以是普通的指針也可以是智能指針。需要注意的是key是唯一的,如果不唯一,會產生一個斷言錯誤,推薦用類型的名稱作為key,可以保證唯一性,std::string strKey = typeid(T).name()。
由於用到了C++11的可變模板參數,所以對編譯器有要求,windows中需要vs2012的Microsoft Visual C++ Compiler Nov 2012 CTP;linux GCC4.7及以上。
測試代碼:
struct ICar { virtual ~ICar(){} virtual void test() const = 0; }; struct Bus : ICar { Bus(int i, float f) {}; void test() const { std::cout << "Bus::test()"; } }; class IX { public: IX(){} virtual ~IX(){} virtual void g()=0; private: }; class X : public IX { public: void g() { std::cout << "it is a test" << std::endl; } }; void TestMyIoc() { //簡單注冊,需要類型信息和key IocContainer ioc; ioc.RegisterType<X>("ss"); ioc.RegisterType<X>("ss");//key重復,會報錯 auto ix = ioc.ResolveShared<X>("ss"); ix->g(); //簡單注冊,需要類型信息、接口類型和key ioc.SimpleRegisterType<IX, X>("ff"); auto ix1 = ioc.ResolveShared<IX>("ff"); ix1->g(); //含參數的類型注冊,需要類型信息、接口類型、參數類型和key ioc.RegisterType<ICar, Bus, int, float>("bus"); int a=1; float b= 2.0; auto mycar = ioc.ResolveShared<ICar>("bus", a, b); mycar->test(); }
測試結果:
it is a test it is a test Bus::test()
補充一點:當resolve的參數類型為指針類型時,直接將nullptr作為參數傳入會報異常,應該講nullptr強制轉換為注冊時的參數類型。
ioc.Resolve<BaseAppChain>(str, (BaseAppChain*)nullptr);