转:自己动手写插件框架(2)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-2/

插件编程接口

所谓插件,其实就是基于接口的设计。基于插件的系统最基本的一点就是,要有一个中心系统,用于加载未知的插件,并且能够使用预先定义好的接口和协议与这些插件进行交互。

最基本的方式是定义一个接口,提供一系列插件(动态的或者是静态)需要暴露出的函数。这种实现从技术上说是可行的,但实际并不那么简单地操作。原因在于,一个插件需要支持两类接口,但是却只能暴露出一个接口的函数集。这意味着,两类接口必须混合在一起。

第一个接口(协议)是通用插件接口。该接口允许中心系统初始化插件,能够将插件提供的用于创建、销毁对象的函数注册给中心系统。这个通用插件接口不是特定领域相关的,因此能够作为一个可复用库。第二个接口则是插件对象提供的功能接口。这个接口是与特定领域相关的,必须被仔细地设计,并且由插件实际实现。中心系统应当利用这个接口与插件对象进行交互。

下面我们给出一个通用插件接口的头文件。这里,我们不会深究细节,仅仅为了有个相对直观地认识。

 1 #ifndef PF_PLUGIN_H
 2 #define PF_PLUGIN_H
 3
 4 #include <apr-1/apr_general.h>
 5
 6 #ifdef __cplusplus
 7 extern "C" {
 8 #endif
 9
10 typedef enum PF_ProgrammingLanguage
11 {
12     PF_ProgrammingLanguage_C,
13     PF_ProgrammingLanguage_CPP,
14 }   PF_ProgrammingLanguage;
15
16 struct PF_PlatformServices_;
17
18 typedef struct PF_ObjectParams
19 {
20     const apr_byte_t * objectType;
21     const struct PF_PlatformServices_ * platformServices;
22 } PF_ObjectParams;
23
24 typedef struct PF_PluginAPI_Version
25 {
26     apr_int32_t major;
27     apr_int32_t minor;
28 } PF_PluginAPI_Version;
29
30 typedef void * (*PF_CreateFunc)(PF_ObjectParams *);
31
32 typedef apr_int32_t (*PF_DestroyFunc)(void *);
33
34 typedef struct PF_RegisterParams
35 {
36     PF_PluginAPI_Version version;
37     PF_CreateFunc createFunc;
38     PF_DestroyFunc destroyFunc;
39     PF_ProgrammingLanguage programmingLanguage;
40 } PF_RegisterParams;
41
42 typedef apr_int32_t (*PF_RegisterFunc)(const apr_byte_t * nodeType,
43                                        const PF_RegisterParams * params);
44
45 typedef apr_int32_t (*PF_InvokeServiceFunc)(const apr_byte_t * serviceName,
46                                             void * serviceParams);
47
48 typedef struct PF_PlatformServices
49 {
50     PF_PluginAPI_Version version;
51     PF_RegisterFunc registerObject;
52     PF_InvokeServiceFunc invokeService;
53 } PF_PlatformServices;
54
55 typedef apr_int32_t (*PF_ExitFunc)();
56
57 typedef PF_ExitFunc (*PF_InitFunc)(const PF_PlatformServices *);
58
59 #ifndef PLUGIN_API
60   #ifdef WIN32
61     #define PLUGIN_API __declspec(dllimport)
62   #else
63     #define PLUGIN_API
64   #endif
65 #endif
66
67 extern
68 #ifdef  __cplusplus
69 "C"
70 #endif
71 PLUGIN_API PF_ExitFunc PF_initPlugin(const PF_PlatformServices * params);
72
73 #ifdef  __cplusplus
74 }
75 #endif
76
77 #endif /* PF_PLUGIN_H */

你需要认识到的第一件事是,这是一个 C 头文件。这就允许我们的插件框架能够被纯 C 系统编译和使用,并且能够编写纯 C 插件。但是,这么做并不会限定必须使用 C,实际上,它已经被设计为更常用 C++ 来实现。

PF_ProgrammingLanguage枚举允许插件告诉插件管理器,它本身是由 C 还是 C++ 实现的。

PF_ObjectParams是一个抽象结构,在创建插件对象时被传入。

PF_PluginAPI_Version用于指明版本信息。这有助于插件管理器只加载兼容版本的插件。

函数指针PF_CreateFuncPF_DestroyFunc必须由插件实现,用于插件管理器创建和销毁插件对象。

PF_RegisterParams结构包含了插件必须提供给插件管理器的所有信息,以便插件管理器初始化插件(版本,创建、销毁函数以及开发语言)。

PF_RegisterFunc函数指针(由插件管理器实现)允许每个插件将其支持的对象类型以PF_RegisterParams结构的形式注册给插件管理器。注意,这种实现允许插件注册不同版本的对象,以及注册多个对象类型。

PF_InvokeService函数指针是一个通用函数,允许插件调用主系统提供的各种服务,例如日志、事件处理或者错误报告等。该函数要求有一个服务名称以及一个指向参数结构的不透明的指针。插件应当知道可用的服务以及如何调用它们(或者实现一种服务发现机制)。

PF_PlatformServices结构用于表示平台提供的所有服务(版本、已注册对象和调用函数)。该结构会在插件初始化的时候传给每一个插件。

PF_ExitFunc是插件退出函数的指针,由插件实现。

PF_InitFunc是插件初始化的函数指针。

PF_initPlugin是动态插件(也就是通过动态链接库或者共享库部署的插件)初始化函数的实际声明。它由动态插件暴露出,所以插件管理器可以在加载插件时进行调用。它有一个指向PF_PlatformServices结构的指针,所以在插件初始化时,这些服务都是可以调用的(这正是注册对象的理想时机),函数返回退出函数的指针。

而对于静态插件(由静态链接库实现,并且直接与主应用程序链接的插件)应该实现init函数,但是不能命名为PF_initPlugin。原因是,如果有多个静态插件,它们不能有相同的名字的函数。

静态插件的初始化过程有所不同。它们必须由主程序显式地进行初始化,也就是通过PF_InitFunc调用其初始化函数。这实际是不好的设计,因为如果要新增或者删除静态插件,主应用的代码都必须修改,并且那些不同名字的init函数都必须能够找到。

有一个叫做“自动注册”的技术试图解决这个问题。自动注册由一个静态库的全局对象实现。该对象会在main()函数执行之前构造完成。这个全局对象能够请求插件管理器初始化静态插件(通过传递插件init()函数的指针来完成)。不幸的是,在某些版本的 Visual C++ 中,这种技术并不支持。

编写插件

如何编写插件?我们的插件框架提供了最通用的功能,在目前的条件下很难添加能够与主应用交互的插件。所以,你必须再次插件框架的基础之上构建自己的应用程序对象。这意味着,你的应用程序(加载插件的)连同插件本身,都必须遵守同一个交互模型。通常这代表,应用程序需要插件提供特定类型的对象,用于暴露某些特定的 API。插件框架提供所有必须的公共基础代码,用于插件的注册、枚举以及加载。

下面的例子是 C++ 接口定义的IActor。这个接口有两个操作:getInitialInfo()play()。注意,这个接口并不足以应付所有情况,因为getInitialInfo()函数需要一个指向ActorInfo结构的指针,而play()则需要另外一个接口ITurn的指针。这是经常遇见的情况,你必须这么设计,并且指定一个特定的对象模型。

1 struct IActor
2 {
3     virtual ~IActor() {}
4     virtual void getInitialInfo(ActorInfo * info) = 0;
5     virtual void play( ITurn * turnInfo) = 0;
6 };

每个插件都可以注册IActor接口的多个实现。当应用程序决定实例化一个由插件注册的对象时,它就调用由插件实现的PF_CreateFunc函数。插件就会做出响应,创建对象并返回给应用程序。函数返回值是void *,因为对象的创建操作是通用插件框架的一部分,因此并不知道任何关于特定的IActor接口的信息。应用程序负责将void *转换成IActor *,然后像其他对象一样通过接口调用其函数。当应用程序使用完IActor对象时,会调用注册的PF_DestroyFunc函数,插件就销毁该对象。至于为什么需要有虚析构函数,我们会在以后的讨论中介绍。

时间: 2024-10-07 00:12:32

转:自己动手写插件框架(2)的相关文章

转:自己动手写插件框架(3)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-3/ 编程语言的支持 在前面的章节中,我们已经了解到,如果你能够使用编译器令应用程序和插件的虚表匹配,那么就可以保持 C++ 虚表级别的兼容性:或者你可以使用 C 级别的兼容性,然后就能使用不用的编译器去构建系统,但其限制在于你只能编写纯 C 应用.这样的话,你就不能够使用前面我们在例子 IActor 中看到的那种优雅的 C++ 接口. 纯 C 实现 在纯 C

转:自己动手写插件框架(1)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-1/ 本系列文章来自 Building Your Own Plugin Framework,主要内容是讨论使用 C/C++ 语言开发跨平台的插件框架所需要的架构.开发方法以及部署.我们将从分析现有插件/组件系统开始,一步步深入了解如何开发插件框架,以及很多需要注意的问题,比如二进制兼容性等,在文章的最后,我们将给出一个比较合理的解决方案. 在本系列文章中,我们

《开源框架那点事儿20》:自己动手写前端框架

曾经发表过一篇文章:TinyAdmin前端展现框架.其在线演示路径为:http://www.tinygroup.org/tinyadmin/.应该说有很多人还是感觉兴趣的,可是因为这个是基于SmartAdmin框架改写的,尽管我们自己买了SmartAdmin的授权,可是广大用户假设要用的时候,就会有授权相关的问题,这会大大影响一些人的使用决策--尤其是会再发行的朋友. 再一个原因是SmartAdmin初看不是不错的.可是实际用起来.里面的问题比較多.对IE8基本上能够说是不兼容,尽管我们努力进行

《自己动手写开源框架10》:Web界面快速开发实践

下面是一些常用的链接,供大家使用: GIT地址:https://git.oschina.net/tinyframework/tiny问题报告:https://git.oschina.net/tinyframework/tiny/issues更多内容,请看本人博客,不一样的内容,一样的精彩! 在展示过程的同时,会把相关的知识做一个充分的介绍 .一.寻找网站模板 要做网站,不能没有模板,自己不会做网页设计,咋办?问谷歌找百度呗,找了一阵,看到下面这个模板不错,就它了. http://www.toop

自己动手写web框架----1

本文可作为<<自己动手写struts–构建基于MVC的Web开发框架>>一书的读书笔记. 一个符合Model 2规范的web框架的架构图应该如下: Controller层的Servlet就是一个全局的大管家,它判断各个请求由谁去处理. 而各个BusinessLogic就决定具体做什么. 通过上面的图,我们能看出来核心的组件就是那个servlet,它要处理所有的请求. 那么我们就先在web.xml里配置这个servlet: <?xml version="1.0&quo

自己动手写web框架----2

在上一节,我们自己写的web框架,只能运行显示一个HelloWorld.现在我们对其进行一次加工,让他至少能运行一个登陆程序. 首先看login.jsp <%@ page contentType="text/html; charset=UTF-8" language="java" import="java.sql.*" errorPage="" %> <%@ page import=" java.u

自己动手写RPC框架到dubbo的服务动态注册,服务路由,负载均衡功能实现

RPC即远程过程调用,它的实现方式有很多,比如webservice等.框架调多了,烦了,没激情了,我们就该问自己,这些框架的作用到底是什么,来找回当初的激情. 一般来说,我们写的系统就是一个单机系统,一个web服务器一个数据库服务,但是当这单台服务器的处理能力受硬件成本的限制,是不能无限的提升处理性能的.这个时候我们使用RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了提升. RPC的实现包括客户端和服务端,即服务的调用方和服务的提供方.服务调用方发送rpc请求

自己动手写javaEE框架-为jqgrid换换皮肤然后加到我的框架里

软件151 苏垚 今天让我倒腾半天的居然不是写我的javaEE框架,而是更换jqgrid的皮肤.我是相当的讨厌jqgrid的默认皮肤,它总让我想起一直想学而都没学过的EXTJS,看多了那种淡蓝色的皮肤太多了绝对视觉疲劳,所以这篇博文就从jqgrid换肤讲起吧,这个过程在网络上找到的资料比较少,因此我今天的博文还是很有实用价值的.不废话了,下面开始吧! 首先我们在浏览器地址栏里填入地址:http://www.trirand.com/blog/?page_id=6,这是jqgrid的下载界面,如下图

第一招:自己动手写HTTP框架介绍

我相信很多人都会质疑:网上已经有那么多开源的HTTP框架,android-async-http已经很成熟了,Stay你再来讲而且还收费有意义么. Stay在这里统一作答. HTTP作为所有app的必备功能,我觉得有必要来讲. 开源框架授之以鱼,我希望能做到授之以渔. 看看android-async-http里的issues,如果你也碰到变态需求,你是否会改,而不是等待作者帮你解. 每个人的理解能力不一样,可能看不懂源码,我希望把骨架拎出来,一个个讲解,让你弄懂它的设计思想. 可能就算你看懂了代码