利用C 11實現乙個自動註冊的工廠

2021-07-09 06:28:36 字數 3515 閱讀 2736

工廠方法是最簡單地建立派生類物件的方法,也是很常用的,工廠方法內部使用switch-case根據不同的key去建立不同的派生類物件,下面是乙個偽**。

message* create(int

type)

}

隨著時間的流逝,訊息種類越來越多,這個switch-case會越來越長,我在乙個開源專案中看到過一百多個case語句,顯然這種簡單工廠已經不堪負荷,這樣的**對於維護者來說也是乙個噩夢。要消除這些長長的switch-case語句是乙個需要解決的問題,而自動註冊的物件工廠則是乙個比較優雅的解決方案。

自動註冊的物件工廠遵循了開放-封閉原則,新增物件時無需修改原有**,僅僅需要擴充套件即可,徹底地消除了switch-case語句。

實現方法

自動註冊的物件工廠的實現思路如下:

提供乙個單例工廠物件。

工廠註冊物件(儲存建立物件的key和構造器)。

利用輔助類,在輔助類物件的構造過程中實現目標物件地註冊。

利用乙個巨集來生成輔助物件。

在派生類檔案中呼叫這個巨集實現自動註冊。

其中,需要注意的是,物件工廠並不直接接存物件,而是物件的構造器,因為物件工廠不是物件池,是物件的生產者,允許不斷地建立例項,另外,這樣做還實現了延遲建立。另外乙個要注意的地方是借助巨集來實現自動註冊,本質上是通過巨集來定義了很多全域性的靜態變數,而這些靜態變數僅僅是為了實現自動註冊,並沒有實際的意義。

下面來看看如何用c++11來實現這個自動註冊的物件工廠。

乙個單例的物件工廠**

struct factory

private:

factory() {};

factory(const factory&) = delete;

factory(factory&&) = delete;

static

std::map

map_;

};

在c++11中單例的實現非常簡單,返回乙個乙個靜態區域性變數的引用即可,而且這個方法還是執行緒安全的,因為c++11中靜態區域性變數的初始化是執行緒安全的。工廠內部有乙個map,map的值型別為乙個function,是物件的構造器。

物件工廠的輔助類的**

struct factory

); }

};private:

inline

static factory& get()

static

std::map

map_;

};

物件工廠的輔助類register_t是工廠類的乙個內部模版類,非常簡單,只有乙個建構函式,這個建構函式中呼叫了factory的私有變數map_,並往map_中插入了key和泛型物件的構造器。這裡用到了c++11的乙個新特性:內部類可以通過外部類的例項訪問外部類的私有成員,所以register_t可以直接訪問factory的私有變數map_。

自動註冊的**

#define register_message_vname(t) reg_msg_##t##_

#define register_message(t, key, ...) static factory::register_tregister_message_vname(t)(key, __va_args__);

在派生類中呼叫巨集註冊自己:

class message1 : public message

;register_message(message1, "message1");

自動註冊的關鍵是通過乙個巨集來生成靜態全域性的register_t的例項,因為register_t的例項是用來向工廠註冊目標物件的構造器。所以僅僅需要在派生類中呼叫這個巨集就可以實現自動至註冊了,而無需修改原有**。

我們還可以新增智慧型指標介面,無需讓使用者管理原始指標,甚至讓工廠能建立帶任意引數的物件。

factory最終的實現

#include 

#include

#include

#include

#include "message.hpp"

struct factory

); }

template

register_t(const

std::string& key, args... args));}

};static message* produce(const

std::string& key)

static

std::unique_ptrproduce_unique(const

std::string& key)

static

std::shared_ptr

produce_shared(const

std::string& key)

private:

factory() {};

factory(const factory&) = delete;

factory(factory&&) = delete;

static factory& get()

static

std::map

map_;

};std::map

factory::map_;

#define register_message_vname(t) reg_msg_##t##_

#define register_message(t, key, ...) static factory::register_tregister_message_vname(t)(key, __va_args__);

示例

class message

virtual

void foo()

};#include "message.hpp"

#include "msgfactory.hpp"

class message1 : public message

message1(int a)

~message1()

void foo() override

};//register_message(message1, "message1", 2);

register_message(message1, "message1");

#include "message1.hpp"

int main()

總結:

使用c++11,僅僅需要幾十行**就可以實現乙個自動註冊的物件工廠,消除了長長的swithc-case語句,還遵循了開閉原則,簡潔而優雅。 

完整的**:

作者簡介: 

祁宇:珠海雲創科技研發中心技術總監,資深c++技術專家。 

致力於c++11的應用、研究和推廣。珠海雲創科技研發中心技術總監,負責公司雲基礎架構的研發。精通oop、ood、設計模式和重構,主要研究方向為架構設計和業務重構,有豐富的開發和研發管理經驗。愛好c++,愛好開源,樂於研究和分享技術,開源了多個專案。

利用C 11實現乙個自動註冊的工廠

工廠方法是最簡單地建立派生類物件的方法,也是很常用的,工廠方法內部使用switch case根據不同的key去建立不同的派生類物件,下面是乙個偽 message create int type 隨著時間的流逝,訊息種類越來越多,這個switch case會越來越長,我在乙個開源專案中看到過一百多個c...

利用C 11實現自動註冊的工廠

工廠方法是最簡單地建立派生類物件的方法,也是很常用的,工廠方法內部使用switch case根據不同的key去建立不同的派生類物件,下面是乙個偽 message create int type 隨著時間的流逝,訊息種類越來越多,這個switch case會越來越長,我在乙個開源專案中看到過一百多個c...

C 11實現乙個簡單的執行緒池

為了不讓手生,邊複習邊手擼了乙個執行緒池,量比較少,如下,用了一些c 11的實現,語言標準嘛,就是跨平台的 thread poo.h ifndef thread pool define thread pool include include include include include inclu...