1. 程式人生 > >C++11之如何實現控制反轉

C++11之如何實現控制反轉

一個小例子
我們先寫一個不使用控制反轉的小例子:

#include <iostream>

using namespace std;

struct A{
    virtual void func(){}
    virtual ~A(){}
};

struct B: public A{
    func(){cout << "B" << endl;}
};

struct C:public A{
    func(){cout << "C" << endl;}
}

class D{
public:
    D(A* a):m_a(a){}

    void
func(){m_a->func();} ~D(){ if (m_a != nullptr) { delete m_a; m_a = nullptr; } } private: A* m_a; }; int _tmain(int argc, _TCHAR* argv[]) { D* active = nullptr; if (condtionB) { active = new D(new B()); } else
{ active = new D(new C()); } delete active; return 0; }

從上面的例子中可以看到D物件和A物件之間的耦合性就產生了。當A物件派生多個子類時,建立D物件時就不得不多加一個條件的判斷,這嚴重影響了程式碼的擴充套件性。也違背了“開放-封閉”的原則。耦合性產生的原因在於D物件的建立直接依賴於new外部物件,也叫做“硬編碼”,是的二者關係緊耦合,失去了靈活性。一種解決辦法是通過工廠模式來建立物件。

struct Factory{
    static A * Create(const string & condition)
    {

        if
(conditionB) { return new B(); } else { return new C(); } } }; int _tmain(int argc, _TCHAR* argv[]) { string condition = "B" D* active = new A(Factory::Create(condition)) active->func(); delete active; return 0; }

這樣改進是不是感覺,以後A增加派生類只需要修改Factory的程式碼就可以了,而不是每次new 的時候都要寫好多判斷語句。但是這個方法還是沒有徹底將兩個物件之間的關係解耦。
要想徹底將兩個物件解耦就要引入一種機制,讓A物件不再直接依賴於外部物件的建立,而是以來某種機制,這種機制可以讓物件之間的關係在外面組裝,外界可以根據需求靈活的配置這種機制對的物件建立策略,而這種機制就是控制反轉(Ioc)。

控制反轉

控制反轉就是應用本身不負責依賴物件的建立和維護,而是交給外部容器來負責。這樣控制權就應由應用賺到了外部的Ioc.從而實現控制反轉。Ioc用來降低物件之間直接依賴產生的耦合性。
從上面的例子中,我們可以看到直接依賴會產生耦合性。

```
void IocSample{
    IocContainer ioc;
    ioc.RegisterType<D,A>("B");
    ioc.RegisterType<D,A>("C");
    //由Ioc容器去初始化D物件
    D* active = ioc.Resolve<D>("B");
    active->func();
    delete active;
};

從上面的例子中,我們通過Ioc配置了D和A物件的關係,然後由Ioc容器去建立A物件,這裡A物件的建立不再依賴於工廠或者A物件,徹底解耦了而知之間的關係。

Ioc讓我們在建立物件上獲得了最大的靈活性,大大降低了依賴物件建立時的耦合性,及時需求變化了,也只需要修改配置檔案就可以建立想要的物件,而不是需要修改程式碼了。通過依賴注入(DI)將建立物件的依賴關係注入到目標型別的建構函式中。就像上面的例子將A依賴於B的依賴關係注入到A型別的建構函式中。

Ioc有兩種能力,一種是物件工廠的能力,不僅可以建立所有的物件,還能根據配置去建立物件;另一種能力是可以去建立依賴物件,應用部需要直接建立以來物件,由Ioc容器去建立實現控制反轉。

簡單Ioc的實現

void IocSample{
    IocContainer ioc;
    ioc.RegisterType<D,A>("B");
    ioc.RegisterType<D,A>("C");
    //由Ioc容器去初始化D物件
    D* active = ioc.Resolve<D>("B");
    active->func();
    delete active;
};

#include <string>
#include <map>
#include <memory>
#include <functional>
using namespace std;

template <class T>
class IocContainer{
public:
    IocContainer(){}
    ~IocContainer(){}

    //註冊需要建立物件的建構函式,需要傳入一個唯一的標識
    template<class Drived>
    void RegisterType(string key){
        std::function<T* ()> function = []{return new Drived();};//lambda表示式
        RegisterType(key, function);
    }

    //根據唯一的標識去查詢對應的構造器,並建立指標物件
    T* Resolve(string key){
        if (m_creator.find(key) == m_creatoe.end())
        {
            return nullptr;
        }
        std::function<T*()> function = m_creator[key];
        return function;
    }
    std::shared_ptr<T> ResolveShared(string key){
        T* ptr = Resolve(key);
        return std::shared_ptr<T>(ptr);
    }

private:
    void RegisterType(string key, std::function<T*()> creator){
        if(m_creator.find(key) != m_creator.end){
            throw std::invalid_argument("this key has already exist!");
        }
        m_creator.emplace(key,creator);
    }
    map<string,std::function<T*()>> m_creator;
};

測試一下

int _tmain(int argc, _TCHAR* argv[])
{
    IocContainer<A> ioc;
    ioc.RegisterType<A>("B");
    ioc.RegisterType<A>("C");

    std::function<A*()> deriveB = ioc.ResolveShared("B");
    deriveB->func();

    std::function<A*()> deriveC = ioc.ResolveShared("C");
    deriveC->func();
    return 0;
}

Result:
B
C

相信大家也注意到一個問題,上面的例子只能建立無參物件,還有一個問題就是隻能建立一種介面型別的物件,不能建立所有型別的物件。下面我們來看看怎麼解決這倆個問題。

Note:型別擦除常用的方法
1)通過多臺來擦除型別
2)通過模板來擦除型別
3)通過某種型別容器來擦除型別
4)通過通用型別來擦除型別
5)通過閉包來擦除型別

感興趣的同學可以看

#include <string>
#include <unorderd_map>
#include <memory>
#include <functional>
using namespace std;
#include <Any.cpp>

struct Car{
    void test ()const{cout << "car" << endl;}

};

struct Bus{
    void test ()const{cout << "bus" << endl;}

};

class IocContainer{
public:
    IocContainer(){}
    ~IocContainer(){}

    //註冊需要建立物件的建構函式,需要傳入一個唯一的標識
    template<class T, typename Depend>
    void RegisterType(const string & key){
        //通過閉包擦除型別
        std::function<T* ()> function = []{return new T(new Depend());};//lambda表示式
        RegisterType(key, function);
    }

    template<class T>
    //根據唯一的標識去查詢對應的構造器,並建立指標物件
    T* Resolve(const string& key){
        if (m_creator.find(key) == m_creatoe.end())
        {
            return nullptr;
        }
        Any resolver = m_creator[key];
        //將找到的any轉化為function
        std::function<T*()> function = resolver.AnyCast<std::function<T*()>>;
        return function;
    }
    template<class T>
    std::shared_ptr<T> ResolveShared(string key){
        T* ptr = Resolve<T>(key);
        return std::shared_ptr<T>(ptr);
    }

private:
    void RegisterType(const string& key, Any creator){
        if(m_creator.find(key) != m_creator.end){
            throw std::invalid_argument("this key has already exist!");
        }
        m_creator.emplace(key,creator);
    }
    unorded_map<string,Any> m_creator;
};

struct A{
    virtual void func(){}
    virtual ~A(){}
};

struct B: public A{
    func(){cout << "B" << endl;}
};

struct C:public A{
    func(){cout << "C" << endl;}
}

class D{
public:
    D(A* a):m_a(a){}

    void func(){m_a->func();}

    ~D(){
        if (m_a != nullptr)
        {
            delete m_a;
            m_a = nullptr;
        }
    }

private:
    A* m_a;
};

int _tmain(int argc, _TCHAR* argv[])
{
    IocContainer ioc;
    //配置依賴關係
    ioc.RegisterType<D,B>("B");
    ioc.RegisterType<D,C >("C");

    auto deriveB = ioc.ResolveShared<D>("B");
    deriveB->func();

    auto deriveC = ioc.ResolveShared<D>("C");
    deriveC->func();

    ioc.RegisterType<Car>("car");
    ioc.RegisterType<Bus>("bus");

    auto bus = ioc.ResolveShared<Bus>("bus");
    bus->test();

    auto car = ioc.ResolveShared<Car>("car");
    car->test();

    return 0;
}:

Result:
B
C
bus
car

我們在最開始的第一個例子上添加了兩個類。然後測試。

建立依賴的物件

配置依賴關係來建立物件

ioc.RegisterType<D,B>("B");
ioc.RegisterType<D,C >("C");  

但是這種方法不太靈活,一旦建立無法修改。所以還有另外一種方法。

配置引數來建立物件

auto B= ioc.ResolveShared<A>("B");
B->func();

以上說的都是Ioc很基礎也較好理解的一些例子,還有一個更好的例子。能適應多種情況的Ioc的容器的實現。大家可以參考《C++深入應用》P275。