在軟件開發中,構建模塊化、可擴展和易維護的應用程序是每位架構師的目標。本文將介紹如何設計和實現一個模塊類來構建一個模塊化編程框架,以便更好地管理和擴展應用程序功能。
引言
在軟件開發中,模塊化編程是一種重要的架構方法。它將應用程序分解為多個相互獨立的模塊,每個模塊負責不同的任務。這種方法使得代碼更易維護、更易擴展,也更易重用。本文將介紹一個簡單而強大的模塊化編程框架,它包括兩個主要組件:模塊類(Module)和模塊工廠類(ModuleFactory)。這兩者的結合為開發者提供了一種靈活的方式,可以輕松地創建和管理模塊。
模塊類(Module)
設計
模塊類是模塊化編程框架的核心。每個模塊都是一個繼承自 Module 的類。模塊類有一個名字屬性,用于唯一標識每個模塊。
class Module {
public:
Module() = default;
virtual ~Module() = default;
const std::string &getModuleName() const {
return moduleName_;
}
protected:
void setModuleName(const std::string &name) {
moduleName_ = name;
}
private:
std::string moduleName_;
friend class ModuleFactory;
};
在上述代碼中,Module 類包括了以下主要部分:
- getModuleName(): 獲取模塊的名字。
- setModuleName(const std::string &name): 設置模塊的名字。
setModuleName 方法的私有訪問權限確保只有 ModuleFactory 類能夠修改模塊的名字。
模塊工廠類(ModuleFactory)
設計
模塊工廠類是一個用于創建和管理模塊的中心樞紐。每個模塊必須在工廠中進行注冊,以便能夠通過工廠創建和獲取模塊的實例。
class ModuleFactory {
private:
// 內部結構體,用于標識模塊
struct moduleid {
moduleid() = default;
moduleid(std::string n, std::size_t h)
: name(std::move(n)), code(h) {}
explicit moduleid(const std::type_info &info)
: name(info.name()), code(info.hash_code()) {}
bool operator==(const moduleid &mid) const {
return name == mid.name && code == mid.code;
}
std::string name;
std::size_t code = 0;
};
public:
// 獲取 ModuleFactory 的單例實例
static ModuleFactory &getFactory() {
static ModuleFactory factory;
return factory;
}
// 獲取注冊在工廠中的模塊名稱集合
const std::unordered_set<std::string> &getModuleNames() const {
return *moduleNames_;
}
// 用于注冊模塊的輔助結構體
template<typename T, typename C = T>
struct ModuleRegisterHelper {
// 構造函數,用于注冊模塊
template<typename... Args>
ModuleRegisterHelper(std::string n, Args &&...args) {
auto mptr = getFactory().moduleMapTable_;
if (mptr->find(n) != mptr->end()) {
throw std::invalid_argument("模塊 " + n + " 已經注冊。");
}
getFactory().moduleNames_->insert(n);
std::function<std::unique_ptr<Module>(Args...)> create_func = [](Args... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
};
const std::any const_any = std::any(create_func);
(*mptr)[n] = std::make_pAIr(moduleid(typeid(C)), const_any);
}
};
// 獲取指定名稱和類型的模塊
template<typename T = Module, typename... Args>
typename std::enable_if<std::is_base_of<Module, T>::value, std::unique_ptr<T>>::type
getModule(const std::string &n, Args &&...args) {
auto mptr = getFactory().moduleMapTable_;
auto miter = mptr->find(n);
if (miter == mptr->end()) {
throw std::runtime_error("找不到名稱為 " + n + " 的模塊。");
}
if (miter->second.first == moduleid(typeid(T))) {
auto moduleCreator = std::any_cast<std::function<std::unique_ptr<Module>(Args...)>>(miter->second.second);
std::unique_ptr<Module> module = moduleCreator(std::forward<Args>(args)...);
module->setModuleName(n);
return std::unique_ptr<T>(static_cast<T *>(module.release()));
}
auto moduleCreator = std::any_cast<std::function<std::unique_ptr<Module>(Args...)>>(miter->second.second);
std::unique_ptr<Module> module = moduleCreator(std::forward<Args>(args)...);
if (!dynamic_cast<T *>(module.get())) {
throw std::runtime_error("無法將模塊 " + n + " 產生的類型轉換為 " + typeid(T).name());
}
module->setModuleName(n);
return std::unique_ptr<T>(static_cast<T *>(module.release()));
}
private:
static std::unordered_map<std::string, std::pair<moduleid, std::any>> *moduleMapTable_;
static std::unordered_set<std::string> *moduleNames_;
ModuleFactory() {}
ModuleFactory(const ModuleFactory &) = delete;
ModuleFactory(ModuleFactory &&) = delete;
};
在 ModuleFactory 類中,可以創建、獲取和管理已注冊的模塊。
注冊模塊
要使用模塊工廠創建模塊,需要首先注冊模塊。這里是通過
ModuleFactory::ModuleRegisterHelper 模板類來實現的。在注冊模塊時,需要提供模塊的類型和名字。
可以使用又愛又恨的宏,避免了手動編寫大量的注冊代碼:
#define MODULE_VARIABLE_NAME(module_name, module_type) Module_##module_name##_##module_type##_
#define REGISTER_MODULE(module_name, module_type, ...)
static module::ModuleFactory::ModuleRegisterHelper<module_type> MODULE_VARIABLE_NAME(module_name, module_type)(#module_name, ##__VA_ARGS__)
#define DECLARE_MODULE(module_type, ...) REGISTER_MODULE(module_type, module_type, ##__VA_ARGS__)
解釋如下:
- MODULE_VARIABLE_NAME(module_name, module_type) 宏:
- 該宏用于生成一個唯一的變量名稱,通常用于防止沖突。它的作用是在模塊名稱和模塊類型之間創建一個唯一的標識符。
- module_name:模塊名稱,通常是一個字符串。
- module_type:模塊類型,通常是一個C++類的類型。
- 例如,如果你有一個模塊名稱為 "MyModule",模塊類型為 "MyModuleType",則調用 MODULE_VARIABLE_NAME 宏后將生成 Module_MyModule_MyModuleType_ 作為標識符。
- REGISTER_MODULE(module_name, module_type, ...) 宏:
- 該宏用于注冊模塊并將其添加到工廠中,以便后續可以根據名稱獲取該模塊。
- module_name:模塊的名稱,通常是一個字符串。
- module_type:模塊的類型,通常是一個C++類的類型。
- ...:可選的額外參數,用于創建模塊對象。
- 該宏的作用是創建一個靜態變量,并使用 MODULE_VARIABLE_NAME 宏生成唯一的變量名稱,然后通過 ModuleFactory::ModuleRegisterHelper 類將模塊注冊到工廠中。這樣,你可以在工廠中使用模塊名稱和類型來創建模塊對象。
- 例如,如果你調用 REGISTER_MODULE("MyModule", MyModuleType, arg1, arg2),宏將創建一個靜態變量 Module_MyModule_MyModuleType_ 并使用 ModuleFactory::ModuleRegisterHelper 來注冊一個名為 "MyModule" 類型為 "MyModuleType" 的模塊,同時傳遞額外的參數 arg1 和 arg2 用于創建該模塊對象。
- 可以很方便的注冊模塊:
REGISTER_MODULE( "MyModule",MyModule);
不喜歡使用宏則可以:
template<typename T, typename... Args>
void registerModule(const std::string &moduleName, Args &&...args) {
static ModuleFactory::ModuleRegisterHelper<T> helper(moduleName, std::forward<Args>(args)...);
}
注冊模塊:
registerModule<MyModuleType>("MyModule", arg1, arg2);
使用模塊
下面是一個示例,展示如何使用模塊類和模塊工廠類來創建和管理模塊。
// 定義一個模塊類
class MyModule : public Module {
public:
// 自定義模塊的行為
};
// 注冊模塊
REGISTER_MODULE( "MyModule",MyModule);
int main() {
// 使用模塊工廠創建模塊
ModuleFactory &factory = ModuleFactory::getFactory();
MyModule *module = factory.getMultiModule<MyModule>("MyModule");
// 使用模塊
if (module) {
std::cout << "Created module: " << module->getModuleName() << std::endl;
}
return 0;
}
項目使用案例:
下面以虛擬商城系統為例,演示如何使用模塊類和模塊工廠類來創建虛擬商城系統,包括購物車管理、用戶注冊和商品管理等多個模塊。
首先,創建購物車管理模塊:
#include "Module.h"
class ShoppingCartModule : public module::Module {
public:
ShoppingCartModule() {
setModuleName("ShoppingCartModule");
}
void execute() override {
std::cout << "Shopping Cart Management Module executed." << std::endl;
// 添加購物車操作
addToCart("Product1", 10.0);
addToCart("Product2", 15.0);
displayCartContents();
calculateTotal();
}
void addToCart(const std::string& item, double price) {
cart.push_back(std::make_pair(item, price));
std::cout << "Added " << item << " to the cart. Price: " << price << " USD" << std::endl;
}
void displayCartContents() {
std::cout << "Cart Contents:" << std::endl;
for (const auto& item : cart) {
std::cout << item.first << " - " << item.second << " USD" << std::endl;
}
}
void calculateTotal() {
double total = 0.0;
for (const auto& item : cart) {
total += item.second;
}
std::cout << "Total Cart Value: " << total << " USD" << std::endl;
}
private:
std::vector<std::pair<std::string, double>> cart;
};
DECLARE_MODULE(ShoppingCartModule);
然后,創建用戶注冊模塊:
#include "Module.h"
class UserRegistrationModule : public module::Module {
public:
UserRegistrationModule() {
setModuleName("UserRegistrationModule");
}
void execute() override {
std::cout << "User Registration Module executed." << std::endl;
// 用戶注冊操作
registerUser("User1");
registerUser("User2");
displayRegisteredUsers();
loginUser("User1");
}
void registerUser(const std::string& username) {
users.push_back(username);
std::cout << "Registered new user: " << username << std::endl;
}
void displayRegisteredUsers() {
std::cout << "Registered Users:" << std::endl;
for (const std::string& user : users) {
std::cout << user << std::endl;
}
}
void loginUser(const std::string& username) {
std::cout << "User " << username << " logged in." << std::endl;
}
private:
std::vector<std::string> users;
};
DECLARE_MODULE(UserRegistrationModule);
最后,創建商品管理模塊:
#include "Module.h"
class ProductManagementModule : public module::Module {
public:
ProductManagementModule() {
setModuleName("ProductManagementModule");
}
void execute() override {
std::cout << "Product Management Module executed." << std::endl;
// 商品管理操作
addProduct("Product1", 10.0, 20);
addProduct("Product2", 15.0, 15);
displayProducts();
showProductDetails("Product1");
}
void addProduct(const std::string& product, double price, int quantity) {
products.push_back(std::make_pair(product, std::make_pair(price, quantity));
std::cout << "Added new product: " << product << " Price: " << price << " USD Quantity: " << quantity << std::endl;
}
void displayProducts() {
std::cout << "Available Products:" << std::endl;
for (const auto& product : products) {
std::cout << product.first << " - Price: " << product.second.first << " USD Quantity: " << product.second.second << std::endl;
}
}
void showProductDetails(const std::string& product) {
for (const auto& p : products) {
if (p.first == product) {
std::cout << "Product Details - " << product << ":" << std::endl;
std::cout << "Price: " << p.second.first << " USD" << std::endl;
std::cout << "Available Quantity: " << p.second.second << std::endl;
return;
}
}
std::cout << "Product not found: " << product << std::endl;
}
private:
std::vector<std::pair<std::string, std::pair<double, int>> products;
};
DECLARE_MODULE(ProductManagementModule);
最終的main函數會非常簡潔:
#include "Module.h"
int main() {
// 獲取模塊工廠實例
module::ModuleFactory& factory = module::ModuleFactory::getFactory();
// 獲取購物車管理模塊并執行
auto shoppingCartModule = factory.getMultiModule<ShoppingCartModule>("ShoppingCartModule");
shoppingCartModule->execute();
// 獲取用戶注冊模塊并執行
auto userRegistrationModule = factory.getMultiModule<UserRegistrationModule>("UserRegistrationModule");
userRegistrationModule->execute();
// 獲取商品管理模塊并執行
auto productManagementModule = factory.getMultiModule<ProductManagementModule>("ProductManagementModule");
productManagementModule->execute();
return 0;
}
結論
模塊化編程是一種強大的架構方法,它使得應用程序更容易維護、擴展和重用。通過使用模塊類和模塊工廠類,能夠更好地管理和擴展應用程序的功能。模塊化編程框架不僅可以幫助架構師構建可擴展的應用程序,還可以幫助開發者編寫更具模塊性的代碼,提高代碼質量和可維護性。
本文雖然簡單,但一個大型框架同樣也是由一招一式構成,大道至簡,藏拙于巧。
參考
參考文獻: [1] Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design patterns: elements of reusable object-oriented software. Pearson Education.
[2] Martin, R. C. (2003). Agile software development, principles, patterns, and practices. Pearson Education.
[3] Fowler, M. (2018). Refactoring: improving the design of existing code. Addison-Wesley Professional.