CPP技巧整理 —— 依賴注入

設(shè)計(jì)模式里面有一個(gè)很重要的思想,原話可能是“不要依賴于具體枫耳,而是要依賴于抽象”怨咪。在軟件的設(shè)計(jì)中屋剑,這種思想可謂算是指導(dǎo)思想了。比如系統(tǒng)要設(shè)計(jì)一個(gè)rpc服務(wù)诗眨。一種比較好的設(shè)計(jì)思路是唉匾,首先提供一個(gè)抽象的類,把需要的方法都放進(jìn)去匠楚。

class RPC
{
public:
  virtual void send(const std::string& text) = 0;
};

然后呢巍膘,比如要接入grpc,那么定義一個(gè)grpc的類芋簿,繼承RPC類典徘。把里面的接口用grpc的實(shí)現(xiàn)封裝一套。

class GRPC final : public RPC 
{
public:
    void send(const std::string& text) override 
    { 
        std::cout << text << std::endl; 
        // todo
        // use grpc to send data
    }
};

如果益咬,以后要再接入其他rpc服務(wù)逮诲,或者更換rpc服務(wù),那么只需要再新定義一個(gè)rpc類幽告。

class TRPC final : public RPC 
{
public:
    void send(const std::string& text) override
    { 
        std::cout << text << std::endl; 
        // todo
        // use trpc to send data
    }
};

如果需要業(yè)務(wù)層需要接入的時(shí)候梅鹦,只需要讓自己依賴于抽象的RPC類,而不是某個(gè)具體的RPC類冗锁。比如這樣:

class MY_SERVICE
{
    public:
        MY_SERVICE()
        {
            // 讀配置或者是什么樣齐唆,選擇不同的rpc類型
            rpc = new TRPC();
        }

        int send(const std::string text)
        {
            rpc->send(text);
            return 0;
        }

    private:
        RPC * rpc;
};

這是一個(gè)簡單的例子,實(shí)現(xiàn)了依賴的解耦冻河。開始進(jìn)入依賴注入的環(huán)節(jié)箍邮。依賴注入的分為“注冊”和“構(gòu)造”兩個(gè)階段茉帅,在注冊的時(shí)候,指定好service和接口的關(guān)系锭弊;在構(gòu)建的時(shí)候堪澎,根據(jù)綁定的關(guān)系,初始化serive對象味滞。

這里有幾點(diǎn)需要開發(fā)樱蛤,第一是:管理各種基類派生出來的子類,或者是根據(jù)不同的配置文件創(chuàng)建不同的子類剑鞍。第二是:需要反射的機(jī)制昨凡,根據(jù)子類的名稱完成對象創(chuàng)建。

比較硬的寫法是蚁署,可以這樣:

COMM_DEP* create_obj(string name)
{
  if (name == "GRPC")
  {
      return new GRPC();
  }
  else if (name == "TRPC")
  {
      return new TRPC();
  }
  else
  {
      xxx;
  }
}

這樣寫法有很多弊端便脊,更好的方式是讓這個(gè)過程更簡潔。這里使用了一個(gè)宏光戈,會(huì)生成一個(gè)字符串和對象的映射map哪痰。然后把它封裝到一個(gè)類中,可以main函數(shù)之外田度,創(chuàng)建這個(gè)類妒御,就可以將對象的字符串和對象映射到map中。

// helper.h
#pragma once

#include <map>
#include <string>

class COMM_DEPENDENCY;

class DEPENDENCY_HELPER
{
private:
    std::map<std::string, COMM_DEPENDENCY *> _map_str2obj;
    static DEPENDENCY_HELPER* helper;
    DEPENDENCY_HELPER() {}

public:
    static DEPENDENCY_HELPER* inst()
    {
        if (!helper)
        {
            helper = new DEPENDENCY_HELPER();
        }
        return helper;
    }

    COMM_DEPENDENCY* get_by_name(std::string name)
    {
        if (_map_str2obj.find(name) != _map_str2obj.end())
        {
            return _map_str2obj[name];
        }
        return nullptr;
    }

    void push(std::string name, COMM_DEPENDENCY * obj) 
    { 
        _map_str2obj[name] = obj; 
    }
};

DEPENDENCY_HELPER* DEPENDENCY_HELPER::helper = nullptr;

#define REGISTER(CLASS_TYPE) \
    class CLASS_TYPE##Generator {\
        public:\
            CLASS_TYPE##Generator() {\
                DEPENDENCY_HELPER::inst()->push(#CLASS_TYPE, new CLASS_TYPE());\
            }\
    };\
    CLASS_TYPE##Generator* CLASS_TYPE##Inst = new CLASS_TYPE##Generator();

對于各種子類镇饺,可以調(diào)用REGISTER(GRPC)來注冊乎莉。

// dep.h
#pragma once

#include "helper.h"

class COMM_DEPENDENCY{};

class RPC: public COMM_DEPENDENCY
{
public:
  virtual void send(const std::string& text) = 0;
};

class GRPC final : public RPC 
{
public:
    void send(const std::string& text) override 
    { 
        std::cout << "[GRPC] " << text << std::endl; 
        // todo
        // use grpc to send data
    }
};

class TRPC final : public RPC 
{
public:
    void send(const std::string& text) override
    { 
       std::cout << "[TRPC] " << text << std::endl; 
        // todo
        // use trpc to send data
    }
};

class LOG: public COMM_DEPENDENCY
{
public:
  virtual void print_log(const std::string& text) = 0;
};

class GLOG final : public LOG 
{
public:
    void print_log(const std::string& text) override 
    { 
        std::cout << "[GLOG] " << text << std::endl; 
    }
};

class XXLOG final: public LOG
{
public:
    void print_log(const std::string& text) override
    { 
        std::cout << "[XXLOG] " << text << std::endl; 
    }
};

// 注冊反射類型
REGISTER(GRPC);
REGISTER(TRPC);
REGISTER(GLOG);
REGISTER(XXLOG);

那么,最后的main函數(shù)就很簡單了奸笤。

#include <iostream>
#include <string>
#include <vector>
#include "helper.h"
#include "dep.h"

class MY_SERVICE
{
public:
    int deps_init()
    {
        // 改為配置加載
        std::vector<std::string> name =
        {
            "GRPC",
            "GLOG",
        };

        for (int i = 0; i < name.size(); i++)
        {
            COMM_DEPENDENCY* obj = DEPENDENCY_HELPER::inst()->get_by_name(name[i]);
            if (obj)
            {
                // init xxx
            }
        }
        return 0;
    }

    RPC* get_rpc()
    {
        RPC* obj = static_cast<RPC*>(DEPENDENCY_HELPER::inst()->get_by_name("GRPC"));
        return obj;
    }

    LOG* get_log()
    {
        LOG* obj = static_cast<LOG*>(DEPENDENCY_HELPER::inst()->get_by_name("GLOG"));
        return obj;
    }
};

int main()
{
    MY_SERVICE s;
    s.get_rpc()->send("HELLO RPC");
    s.get_log()->print_log("HELLO LOG");
    return 0;
}

g++ main.cc                       
$ ./a.out                               
[GRPC] HELLO RPC
[GLOG] HELLO LOG

源碼路徑:
https://github.com/zhaozhengcoder/CoderNoteBook/tree/master/example_code/reflect

小結(jié):
本文整理了cpp開發(fā)中的一個(gè)技巧惋啃,依賴注入的核心思想。這里通過實(shí)現(xiàn)一個(gè)RPC的例子监右,來具體的介紹边灭。另外,為了實(shí)現(xiàn)根據(jù)字符串去創(chuàng)建對象健盒,這里利用宏實(shí)現(xiàn)了一個(gè)簡單的反射機(jī)制绒瘦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扣癣,隨后出現(xiàn)的幾起案子惰帽,更是在濱河造成了極大的恐慌,老刑警劉巖父虑,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件该酗,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呜魄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門悔叽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爵嗅,你說我怎么就攤上這事娇澎。” “怎么了操骡?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵九火,是天一觀的道長赚窃。 經(jīng)常有香客問我册招,道長,這世上最難降的妖魔是什么勒极? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任是掰,我火速辦了婚禮,結(jié)果婚禮上辱匿,老公的妹妹穿的比我還像新娘键痛。我一直安慰自己,他們只是感情好匾七,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布絮短。 她就那樣靜靜地躺著,像睡著了一般昨忆。 火紅的嫁衣襯著肌膚如雪丁频。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天邑贴,我揣著相機(jī)與錄音席里,去河邊找鬼。 笑死拢驾,一個(gè)胖子當(dāng)著我的面吹牛奖磁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播繁疤,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咖为,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了稠腊?” 一聲冷哼從身側(cè)響起躁染,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎麻养,沒想到半個(gè)月后褐啡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鳖昌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年备畦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了低飒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懂盐,死狀恐怖褥赊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莉恼,我是刑警寧澤拌喉,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站俐银,受9級特大地震影響尿背,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捶惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一田藐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吱七,春花似錦汽久、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吝岭,卻和暖如春三痰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苍碟。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工酒觅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人微峰。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓舷丹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜓肆。 傳聞我的和親對象是個(gè)殘疾皇子颜凯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容