<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++11 實現(xiàn)一個自動注冊的工廠

          共 3914字,需瀏覽 8分鐘

           ·

          2021-10-28 11:46

          之前在項目代碼里面看到同事寫了個自動注冊的工廠類,雖然當時我看不懂,但我大受震撼。

          今天又重新溫習(xí)了一下這種寫法,分享給大家,可見學(xué)好 C++ 是多么的重要。

          實現(xiàn)動機

          工廠方法是最簡單地創(chuàng)建派生類對象的方法,也是很常用的,工廠方法內(nèi)部使用switch-case根據(jù)不同的key去創(chuàng)建不同的派生類對象,下面是一個偽代碼。

          Message*?create(int?type)
          {
          ????switch?(type)?
          ????{
          ????case?MSG_PGSTATS:
          ????????m?=?new?MPGStats;
          ????????break;
          ????case?MSG_PGSTATSACK:
          ????????m?=?new?MPGStatsAck;
          ????????break;
          ????case?CEPH_MSG_STATFS:
          ????????m?=?new?MStatfs;
          ????????break;
          ????case?CEPH_MSG_STATFS_REPLY:
          ????????m?=?new?MStatfsReply;
          ????????break;
          ????case?MSG_GETPOOLSTATS:
          ????????m?=?new?MGetPoolStats;
          ????????break;
          ????default:
          ????????break;
          ????}
          }

          隨著時間的流逝,消息種類越來越多,這個switch-case會越來越長,我在一個開源項目中看到過一百多個case語句,顯然這種簡單工廠已經(jīng)不堪負荷,這樣的代碼對于維護者來說也是一個噩夢。

          要消除這些長長的switch-case語句是一個需要解決的問題,而自動注冊的對象工廠則是一個比較優(yōu)雅的解決方案。

          自動注冊的對象工廠遵循了開放-封閉原則,新增對象時無需修改原有代碼,僅僅需要擴展即可,徹底地消除了switch-case語句。

          實現(xiàn)方法

          自動注冊的對象工廠的實現(xiàn)思路如下:

          1. 提供一個單例工廠對象。
          2. 工廠注冊對象(保存創(chuàng)建對象的key和構(gòu)造器)。
          3. 利用輔助類,在輔助類對象的構(gòu)造過程中實現(xiàn)目標對象地注冊。
          4. 利用一個宏來生成輔助對象。
          5. 在派生類文件中調(diào)用這個宏實現(xiàn)自動注冊。

          其中,需要注意的是,對象工廠并不直接保存對象,而是對象的構(gòu)造器,因為對象工廠不是對象池,是對象的生產(chǎn)者,允許不斷地創(chuàng)建實例,另外,這樣做還實現(xiàn)了延遲創(chuàng)建

          另外一個要注意的地方是借助宏來實現(xiàn)自動注冊,本質(zhì)上是通過宏來定義了很多全局的靜態(tài)變量,而這些靜態(tài)變量僅僅是為了實現(xiàn)自動注冊,并沒有實際的意義。

          下面來看看如何用 C++11 來實現(xiàn)這個自動注冊的對象工廠。

          一個單例的對象工廠代碼

          struct?factory
          {

          ????static?factory&?get()
          ????
          {
          ????????static?factory?instance;
          ????????return?instance;
          ????}
          private:
          ????factory()?{};
          ????factory(const?factory&)?=?delete;
          ????factory(factory&&)?=?delete;
          ????static?std::map<std::string,?std::function>?map_;?
          };

          在C++11中單例的實現(xiàn)非常簡單,返回一個一個靜態(tài)局部變量的引用即可,而且這個方法還是線程安全的,因為C++11中靜態(tài)局部變量的初始化是線程安全的。

          工廠內(nèi)部有一個map,map的值類型為一個function,是對象的構(gòu)造器。

          對象工廠的輔助類的代碼

          struct?factory
          {

          ????template<typename?T>
          ????struct?register_t
          ????{

          ????????register_t(const?std::string&?key)
          ????????{
          ????????????factory::get().map_.emplace(key,?[]{?return?new?T;?});
          ????????}
          ????};
          private:
          ????inline?static?factory&?get()
          ????
          {
          ????????static?factory?instance;
          ????????return?instance;
          ????}
          ????
          ????static?std::map<std::string,?FunPtr>?map_;
          };

          對象工廠的輔助類register_t是工廠類的一個內(nèi)部模版類,非常簡單,只有一個構(gòu)造函數(shù),這個構(gòu)造函數(shù)中調(diào)用了factory的私有變量map_,并往map_中插入了key和泛型對象的構(gòu)造器。

          這里用到了C++11的一個新特性:內(nèi)部類可以通過外部類的實例訪問外部類的私有成員,所以register_t可以直接訪問factory的私有變量map_。

          自動注冊的代碼

          #define?REGISTER_MESSAGE_VNAME(T)?reg_msg_##T##_
          #define?REGISTER_MESSAGE(T,?key,?...)?static?factory::register_t?REGISTER_MESSAGE_VNAME(T)(key,?__VA_ARGS__);

          在派生類中調(diào)用宏注冊自己:

          class?Message1?:?public?Message
          {
          ????//……
          };

          REGISTER_MESSAGE(Message1,?"message1");

          自動注冊的關(guān)鍵是通過一個宏來生成靜態(tài)全局的register_t的實例,因為register_t的實例是用來向工廠注冊目標對象的構(gòu)造器。

          所以僅僅需要在派生類中調(diào)用這個宏就可以實現(xiàn)自動至注冊了,而無需修改原有代碼。

          我們還可以添加智能指針接口,無需讓用戶管理原始指針,甚至讓工廠能創(chuàng)建帶任意參數(shù)的對象。

          Factory最終的實現(xiàn)

          #include?
          #include?
          #include?
          #include?
          #include?"Message.hpp"

          struct?factory
          {

          ????template<typename?T>
          ????struct?register_t
          ????{

          ????????register_t(const?std::string&?key)
          ????????{
          ????????????factory::get().map_.emplace(key,?[]?{?return?new?T();?});
          ????????}

          ????????template<typename...?Args>
          ????????register_t(const?std::string&?key,?Args...?args)
          ????????
          {
          ????????????factory::get().map_.emplace(key,?[&]?{?return?new?T(args...);?});
          ????????}
          ????};

          ????static?Message*?produce(const?std::string&?key)
          ????
          {
          ????????if?(map_.find(key)?==?map_.end())
          ????????????throw?std::invalid_argument("the?message?key?is?not?exist!");

          ????????return?map_[key]();
          ????}

          ????static?std::unique_ptr?produce_unique(const?std::string&?key)
          ????
          {
          ????????return?std::unique_ptr(produce(key));
          ????}

          ????static?std::shared_ptr?produce_shared(const?std::string&?key)
          ????
          {
          ????????return?std::shared_ptr(produce(key));
          ????}

          private:
          ????factory()?{};
          ????factory(const?factory&)?=?delete;
          ????factory(factory&&)?=?delete;

          ????static?factory&?get()
          ????
          {
          ????????static?factory?instance;
          ????????return?instance;
          ????}

          ????static?std::map<std::string,?std::function>?map_;
          };

          std::map<std::string,?std::function>?factory::map_;

          #define?REGISTER_MESSAGE_VNAME(T)?reg_msg_##T##_
          #define?REGISTER_MESSAGE(T,?key,?...)?static?factory::register_t?REGISTER_MESSAGE_VNAME(T)(key,?##__VA_ARGS__);

          示例:

          class?Message
          {

          public:
          ????virtual?~Message()?{}

          ????virtual?void?foo()
          ????
          {

          ????}
          };
          #include?"MessageFactory.hpp"
          #include?"Message.hpp"

          class?Message1?:?public?Message
          {
          public:

          ????Message1()
          ????{
          ????????std::cout?<"message1"?<std::endl;
          ????}

          ????Message1(int?a)
          ????{
          ????????std::cout?<"message1"?<std::endl;
          ????}

          ????~Message1()
          ????{
          ????}

          ????void?foo()?override
          ????
          {
          ????????std::cout?<"message1"?<std::endl;
          ????}
          };

          //REGISTER_MESSAGE(Message1,?"message1",?2);
          REGISTER_MESSAGE(Message1,?"message1");

          #include?"Message1.hpp"

          int?main()
          {
          ????Message*?p?=?factory::produce("message1");
          ????p->foo();???//Message1
          ????
          ????auto?p2?=?factory::produce_unique("message1");
          ????p2->foo();
          }

          總結(jié):

          使用C++11,僅僅需要幾十行代碼就可以實現(xiàn)一個自動注冊的對象工廠,消除了長長的swithc-case語句,還遵循了開閉原則,簡潔而優(yōu)雅。

          完整代碼可以參考:

          https://github.com/qicosmos/cosmos/tree/master/self-register-factory

          作者:qicosmos

          地址:https://www.cnblogs.com/qicosmos/p/5090159.html


          技術(shù)交流,歡迎加我微信:ezglumes ,拉你入技術(shù)交流群。

          私信領(lǐng)取相關(guān)資料

          推薦閱讀:

          音視頻開發(fā)工作經(jīng)驗分享 || 視頻版

          OpenGL ES 學(xué)習(xí)資源分享

          開通專輯 | 細數(shù)那些年寫過的技術(shù)文章專輯

          NDK 學(xué)習(xí)進階免費視頻來了

          你想要的音視頻開發(fā)資料庫來了

          推薦幾個堪稱教科書級別的 Android 音視頻入門項目

          覺得不錯,點個在看唄~

          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久另类TS人妖一区二区 | 欧美久久在线观看 | 国内一级黄色片 | 四虎影院永久在线 | 手机在线看片日韩 |