簡介
插件是基於開發人員預先定義好的標準接口開發的獨立部件,windows平臺下多以dll形式呈現
有的公司在軟件開發中,並不會做軟件設計,更沒有相關文檔,純粹想到哪寫到哪,當軟件完工進入維護/功能擴展階段後,如果因爲某種原因,新的人員進入軟件改進。那麼恭喜,新人員擴展功能非常困難,還得承擔相當的破壞現有功能的風險
使用插件系統可以一定程度上解決上述問題,當系統需要的功能不變,僅需變更實現方式的時候,插件系統表現出色,使用插件的好處很多,這裏主要說以下2點:
- 擴展性強。標準接口的新的實現,只需要做一個插件
- 更新量小。功能的更新只需要更新插件,避免了重新發布整個應用程序
使用插件應特別注意:插件API接口應保持長期不變,如果API接口需要不斷變更,不能使用插件系統來開發應用程序
代碼示例
下面用c++代碼描述一個最簡單的插件系統
插件管理器
插件管理器負責插件的安裝,註銷,插件對象的獲取,一般使用線程安全的單例模式實現
class CPluginManager
{
public:
static CPluginManager* GetInstance();
bool Install(wstring name);
void Uninstall(wstring name);
void Uninstall();
CPlugin* GetPlugin(wstring name);
void SetPluginPath(wstring sPluginPath);
wstring GetPluginPath();
private:
CPluginManager();
~CPluginManager();
map<wstring, HINSTANCE> m_mapPlugs;
wstring m_sPluginPath;
static CPluginManager* pInstance;
};
安裝插件
bool CPluginManager::Install(wstring name)
{
if (m_mapPlugs.find(name) != m_mapPlugs.end())
{
return true;
}
wstring path = m_sPluginPath + name;
HINSTANCE instance = LoadLibraryW(path.c_str());
if (instance)
{
m_mapPlugs[name] = instance;
return true;
}
return false;
}
註銷插件
void CPluginManager::Uninstall(wstring name)
{
if (m_mapPlugs.find(name) == m_mapPlugs.end())
{
return ;
}
for (auto it = m_mapPlugs.begin(); it != m_mapPlugs.end();)
{
if (it->first == name)
{
FreeLibrary(it->second);
it = m_mapPlugs.erase(it);
}
else
{
it++;
}
}
return ;
}
獲取插件對象
CPlugin* CPluginManager::GetPlugin(wstring name)
{
if (m_mapPlugs.find(name) == m_mapPlugs.end())
{
return nullptr;
}
Fn f = (Fn)GetProcAddress(m_mapPlugs[name], "?GetInstance@@YAPAVCPlugin@@XZ");
if (f)
{
return f();
}
return nullptr;
}
抽象插件類
此類定義插件支持的所有功能
class CPlugin
{
public:
virtual ~CPlugin() {};
virtual void Show() = 0;
virtual void Release() = 0;
};
插件1
class CPlugin1 : public CPlugin
{
public:
virtual void Show() { MessageBox(NULL, TEXT("this is plugin1"), TEXT("test"), MB_OK); }
virtual void Release() { delete this; }
};
__declspec(dllexport) CPlugin* GetInstance()
{
return new CPlugin1;
}
插件2
class CPlugin2 : public CPlugin
{
public:
virtual void Show() { MessageBox(NULL, TEXT("this is plugin2"), TEXT("test"), MB_OK); }
virtual void Release() { delete this; }
};
__declspec(dllexport) CPlugin* GetInstance()
{
return new CPlugin2;
}
簡單測試
CPluginManager* pm = CPluginManager::GetInstance();
pm->Install(L"dll1.dll");
pm->Install(L"dll2.dll");
CPlugin* p1 = pm->GetPlugin(L"dll1.dll");
CPlugin* p2 = pm->GetPlugin(L"dll2.dll");
p1->Show();
p2->Show();
p1->Release();
p2->Release();
pm->Uninstall(L"dll1.dll");
pm->Uninstall(L"dll2.dll");
插件管理器完整實現
#include "PluginManager.h"
typedef CPlugin* (*Fn)();
CPluginManager* CPluginManager::pInstance = new CPluginManager;
CPluginManager::CPluginManager()
{
m_sPluginPath = L"d:/plugins/";
}
CPluginManager::~CPluginManager()
{
}
CPluginManager* CPluginManager::GetInstance()
{
return pInstance;
}
bool CPluginManager::Install(wstring name)
{
if (m_mapPlugs.find(name) != m_mapPlugs.end())
{
return true;
}
wstring path = m_sPluginPath + name;
HINSTANCE instance = LoadLibraryW(path.c_str());
if (instance)
{
m_mapPlugs[name] = instance;
return true;
}
return false;
}
void CPluginManager::Uninstall(wstring name)
{
if (m_mapPlugs.find(name) == m_mapPlugs.end())
{
return ;
}
for (auto it = m_mapPlugs.begin(); it != m_mapPlugs.end();)
{
if (it->first == name)
{
FreeLibrary(it->second);
it = m_mapPlugs.erase(it);
}
else
{
it++;
}
}
return ;
}
CPlugin* CPluginManager::GetPlugin(wstring name)
{
if (m_mapPlugs.find(name) == m_mapPlugs.end())
{
return nullptr;
}
Fn f = (Fn)GetProcAddress(m_mapPlugs[name], "?GetInstance@@YAPAVCPlugin@@XZ");
if (f)
{
return f();
}
return nullptr;
}
void CPluginManager::Uninstall()
{
for (auto it = m_mapPlugs.begin(); it != m_mapPlugs.end();)
{
FreeLibrary(it->second);
it = m_mapPlugs.erase(it);
}
return ;
}
void CPluginManager::SetPluginPath(wstring sPluginPath)
{
m_sPluginPath = sPluginPath;
}
wstring CPluginManager::GetPluginPath()
{
return m_sPluginPath;
}