一、碎碎念? ?? ??
首先先考慮下,什么情況下軟件需要重構?我覺得答案有很多種,而且還有范圍。當日益增長的需求與現有軟件結構越來越無法匹配時——①具體表現可能為新增需求所導致的bug越來越多,一個新功能的改動牽一發而動全身,需要對代碼較為熟悉才能修改;②現有軟件結構無法滿足一些新需求,軟件結構設計之初未考慮,后續改動工程量巨大③新老員工多人維護代碼,代碼走讀形同虛設,代碼越來越亂 .? ? ? ??
最后產品在殘酷的市場競爭中存活了下來,面對著越來越離譜的代碼,需要著手重構了。面對這一坨代碼,是在現有代碼上重構,還是直接推導重做不兼容,看具體情況了。一般只有行業頭部的公司才會搞重構,小公司正忙著生存。
結合上篇文章軟件插件化,選用里面最后一種方式,將每個模塊作為一個動態庫,理清各個模塊之間的調用關系,將整體代碼重新梳理解耦來實現“重構”。突然想到,我們每一個個體,正如這一坨坨的代碼,只有先生存了下去,才有機會考慮是否能重構自我,還怪殘酷的咧~頭部公司可以投入資源去搞重構,而小公司往往就沒這樣的機會了。
二、插件(組件、中間件)化代碼框架
1、接口定義(核心頭文件)
// plugin_interface.h#ifndef?PLUGIN_INTERFACE_H#define?PLUGIN_INTERFACE_H?class?Plugin?{public:? ??virtual?~Plugin() {} ?// 必須要有虛析構函數? ??virtual?void?initialize()?=?0;? ??virtual?void?execute(const?std::string& params)?=?0;? ??virtual?const?char*?name()?const?=?0;};?// 定義插件創建和銷毀的函數指針類型extern?"C"?{? ??typedef?Plugin* (*CreatePluginFunc)();? ??typedef?void?(*DestroyPluginFunc)(Plugin*);}?#endif
2、插件實現(編寫具體插件)
// demo_plugin.cpp#include?"plugin_interface.h"#include?class?DemoPlugin?:?public?Plugin {public:? ??void?initialize()?override?{? ? ? ? std::cout <<?"DemoPlugin initialized\n";? ? }? ??? ??void?execute(const?std::string& params)?override?{? ? ? ? std::cout <<?"Executing with params: "?<< params <<?"\n";? ? }? ??? ??const?char*?name()?const?override?{? ? ? ??return?"DemoPlugin v1.0";? ? }};
// 導出C接口的創建/銷毀函數extern?"C"?{? ??Plugin*?create_plugin() {? ? ? ??return?new?DemoPlugin();? ? }? ??? ??void?destroy_plugin(Plugin* p) {? ? ? ??delete?p;? ? }}
3、主程序(動態加載插件)
// main.cpp#include "plugin_interface.h"#include#include#include?int?main() {? ??// 1. 加載動態庫? ??void* handle =?dlopen("./demo_plugin.so", RTLD_LAZY);? ??if?(!handle) {? ? ? ? std::cerr?<<?"Error loading plugin: "?<<?dlerror() << std::endl;? ? ? ??return?1;? ? }?? ??// 2. 獲取符號地址? ? auto create = (CreatePluginFunc)dlsym(handle,?"create_plugin");? ? auto destroy = (DestroyPluginFunc)dlsym(handle,?"destroy_plugin");? ??? ??if?(!create || !destroy) {? ? ? ? std::cerr?<<?"Error loading symbols: "?<<?dlerror() << std::endl;? ? ? ??dlclose(handle);? ? ? ??return?1;? ? }?? ??// 3. 創建插件實例? ? std::unique_ptr<Plugin, </Plugin,void(*)(Plugin*)>?plugin(create(), destroy);? ??? ??// 4. 使用插件功能? ? plugin->initialize();? ? plugin->execute("test_parameters");? ? std::cout?<<?"Plugin name: "?<< plugin->name() << std::endl;?? ??// 5. 自動釋放資源(通過unique_ptr)? ??dlclose(handle);? ??return?0;}
4、編譯與運行
4.1 編譯插件(生成 .so 文件)
g++ -fPIC -shared -o demo_plugin.so demo_plugin.cpp
4.2 編譯主程序
g++?main.cpp?-o?main?-ldl
4.3 運行程序
./main# 輸出:# DemoPlugin initialized# Executing with params: test_parameters# Plugin name: DemoPlugin v1.0
三、關鍵點解析
1. ABI 兼容性
使用 extern "C" 確保符號名稱不被改編
保持接口頭文件穩定(修改后需重新編譯所有插件)
2. 資源管理
使用 unique_ptr + 自定義刪除器自動管理插件生命周期
必須通過插件的 destroy_plugin 釋放內存
3. 錯誤處理
檢查 dlopen 和 dlsym 返回值
使用 dlerror() 獲取詳細錯誤信息
歡迎關注,下次分享一個實際使用的例子。