最近接触到插件式编程,感觉是一种不错的思想。分享给大家
两种实现
- Golang的插件式编程
- C++的插件式编程
Golang的插件式编程
- 实现原理
Golang的插件式实现主要依赖于自身的断言机制和接口实现。通过统一插件的接口后,规定实现的接口必须满足指定接口的实现,然后注册到插件管理类,外部使用时直接通过插件管理对象实现调用指定的插件。
注意:插件式编程与插件是有区别的,插件式编程是一种规范,插件是对某一需求的完整实现。 - 源码示例
2.1 接口的定义
package plugin
// 插件必须实现的接口
type (
Plugin interface {
Name() string
}
InitPlugin interface {
Init() error
}
DoPlugin interface {
Do() error
}
)
2.2 注册插件、初始化插件、插件调用的实现
package plugin
import (
"sync"
"fmt"
)
var Pc = struct {
plugins map[string]Plugin // 存放插件,插件名唯一<->插件
mu sync.Mutex
}{
plugins:make(map[string]Plugin),
}
// 注册插件
func RegisterPlugin(plugins ...Plugin){
if len(plugins) == 0{
return
}
Pc.mu.Lock()
for _,plugin := range plugins {
if plugin == nil {
fmt.Println("plugin cannot be nil")
return
}
if _,ok := Pc.plugins[plugin.Name()]; ok {
fmt.Println("repeat add plugin: %s", plugin.Name())
return
}
Pc.plugins[plugin.Name()] = plugin
}
Pc.mu.Unlock()
}
// 调用左右插件的Init方法,初始化插件
func Init() error{
for _,plugin := range Pc.plugins {
if _plugin, ok := plugin.(InitPlugin); ok {
if err := _plugin.Init(); err != nil {
fmt.Printf("pluginContainer Init:%s, err:%v", plugin.Name(), err)
return err
}
}
}
return nil
}
// 执行指定插件的Do操作
func Do(pluginName string) error {
if plugin,ok := Pc.plugins[pluginName]; ok {
if _plugin, ok := plugin.(DoPlugin); ok {
if err := _plugin.Do(); err != nil {
fmt.Println("pluginContainer Do:%s, err:%v", pluginName, err)
return err
}
}
}
return nil
}
// 执行所有插件的Do操作
func DoAll() error {
for _,plugin := range Pc.plugins {
if _plugin, ok := plugin.(DoPlugin); ok {
if err := _plugin.Do(); err != nil {
fmt.Printf("pluginContainer Init:%s, err:%v", plugin.Name(), err)
return err
}
}
}
return nil
}
3.1 现在我们来实现两个简单插件(hello.go,study.go),并在main.go中将实现的两个插件注册(RegisterPlugin)到插件管理中,并实现调用(Do)
hello.go
package hello
import "fmt"
var (
PluginName = "HelloPlugin" // 插件名
)
type Hello struct {
say string
}
func (p *Hello)Name() string {
return PluginName
}
func (p *Hello)Init() error {
fmt.Printf("init %s...\n", PluginName)
p.say = "Hello World!"
return nil
}
func (p *Hello)Do() error{
fmt.Println(p.say)
return nil
}
study.go
package study
import "fmt"
var (
PluginName = "StudyPlugin"
)
type Study struct {
study string
}
func (p *Study)Name() string{
return PluginName
}
func (p *Study)Init() error{
p.study = "Study English!"
return nil
}
func (p *Study)Do() error{
fmt.Println(p.study)
return nil
}
main.go
package main
import (
"../plugin/hello"
"../plugin/plugins"
"../plugin/study"
)
func main() {
plugin.RegisterPlugin(new(hello.Hello), new(study.Study)) // 添加插件
plugin.Init() // 初始化所有插件对象
plugin.Do(hello.PluginName) // 调用hello插件的Do方法
plugin.Do(study.PluginName) // 调用study插件的Do方法
plugin.DoAll() // 调用所有插件的Do方法
}
运行结果:
$ go run main.go
init HelloPlugin…
Hello World!
Study English!
Hello World!
Study English!
C++的插件式编程
- 实现原理
其实无论C++还是Golang对于插件式编程的实现,我觉得都是原理大同小异。只是不同的语言之间对某些专有名字的描述有差别。
C++依赖于虚基类(多态),子类必须继承所需要实现的接口的虚基类,并实现其中的虚函数。调用的实现,即通过基类指针调用实现的虚函数。 - 源码示例
2.1 接口定义 Interface.h
#pragma once
class CPluginInterface
{
public:
virtual char* Name()= 0;
virtual bool Init() = 0;
virtual bool Do() = 0;
};
2.2 插件管理类 pluginMgr
#pragma once
#include "Interface.h"
#include <map>
using std::map;
class CPluginMgr
{
public:
CPluginMgr();
~CPluginMgr();
void RegisterPlugin(CPluginInterface* plugin);
void Init();
void Do(char* pluginName);
void DoAll();
private:
map<char*, CPluginInterface*> m_pluginMap;
bool m_target;
};
#include "pluginMgr.h"
CPluginMgr::CPluginMgr()
{
}
CPluginMgr::~CPluginMgr()
{
}
void CPluginMgr::RegisterPlugin(CPluginInterface* plugin)
{
if (plugin) {
m_pluginMap[plugin->Name()] = plugin;
} else {
printf("register plugin failed.");
}
}
void CPluginMgr::Init()
{
map<char*, CPluginInterface*>::iterator it = m_pluginMap.begin();
while (it != m_pluginMap.end()) {
printf("init %s\n", it->first);
it->second->Init();
it++;
}
}
void CPluginMgr::Do(char * pluginName)
{
CPluginInterface* plugin = m_pluginMap[pluginName];
if (plugin) {
plugin->Do();
} else {
printf("plugin %s Do failed.\n", pluginName);
}
}
void CPluginMgr::DoAll()
{
map<char*, CPluginInterface*>::iterator it = m_pluginMap.begin();
while (it != m_pluginMap.end()) {
it->second->Do();
it++;
}
}
2.3 插件Hello的是实现
#pragma once
#include "Interface.h"
class CHello:public CPluginInterface
{
public:
CHello();
~CHello();
virtual char* Name() override;
virtual bool Init() override;
virtual bool Do() override;
private:
char* m_pluginName;
char* m_say;
};
#include "Hello.h"
#include <stdio.h>
#include <string.h>
CHello::CHello()
{
m_say = new char[32]();
m_pluginName = new char[32]();
int len = strlen("HelloPlugin") + 1;
strcpy_s(m_pluginName, len, "HelloPlugin");
}
CHello::~CHello()
{
if (m_pluginName){
delete m_pluginName;
}
if (m_say) {
delete m_say;
}
}
char * CHello::Name()
{
return m_pluginName;
}
bool CHello::Init()
{
strcpy_s(m_say, strlen("Hello World!")+1, "Hello World!");
return true;
}
bool CHello::Do()
{
printf("%s\n", m_say);
return true;
}
2.4 main函数实现。注册Hello插件到插件管理类中,以及调用插件
#include <iostream>
#include "Hello.h"
#include "pluginMgr.h"
int main()
{
CPluginMgr* mgr = new CPluginMgr(); // 初始化插件管理类
CHello* h = new CHello(); // 初始化插件
mgr->RegisterPlugin(h); // 注册Hello插件到管理对象中
mgr->Init(); // 初始化所有的插件
mgr->Do(h->Name()); // 运行Hello插件
system("pause");
}
完整源码:https://download.csdn.net/download/qq_30145355/10822584