.Net插件框架的实现及分析(一)
2010-09-19 11:15 w i n s o n 阅读(1918) 评论(4) 编辑 收藏 举报
在开始自己系统的同时,总想将系统做得更灵活,可让其他人非常方便地参与进来,这就需要一个完善的插件系统,今天我准备写一个插件框架系列的文章,主要是想记录下我学习 ScrewTurn Wiki 系统的成果,也方便需要的人查阅
我个人感觉 ScrewTurn Wiki 系统的插件框架做得相当好,可让后期制作插件的人方便地将系统进行整合,所以这段时间都在研究。现大概说一下一个插件框架的架构和需求吧。 我认为一个插件系统,应该可以独立于现有系统的类库,让第三方开发者只引用一个插件项目的DLL即可完成所有相关的开发,而且对系统框架要有一定的、统一的约定,不能让第三方开发者想怎样设计就怎样设计,因此一般的插件框架都是接口来实现会比较好,以下将说说大致的调用流程,呵,我就不画流程图啦(主要是我也懒得安装 ),只用文字说明一下吧:
用户访问网站 --> 调用核心功能函数 --> 调用实现此核心功能的接口
因此通过实现不同的接口,就可以方便地实现不同的功能函数,也达到了实现不同插件的效果,这个道理很简单,只要有接触过OO的人都会知道,但具体要如何实现呢,以下将慢慢道来。 此插件框架主要使用了接口+Provider模式实现,因此需先设计好要被实现的接口,以下仅以一个格式化的功能为例吧,此格式化功能插件主要是在文章发布后对内容进行格式化处理,如可为此创建代码高亮插件、查看次数插件等,都是在文章输出时调用不同的格式化插件进行处理即可。 OK,第一步我们先定义好一个全局总的 IProvider 接口,好让之后所有插件都基于这些接口进行:
IProvider.cs 文件:
{
public interface IProvider
{
/// <summary>
/// 初始化插件
/// </summary>
/// <param name="host">主系统对象,可让插件调用主系统里的方法,以达到解耦合</param>
/// <param name="config">插件配置文件</param>
void Init(IHost host, string config);
/// <summary>
/// 当注销插件时调用
/// </summary>
void Shutdown();
/// <summary>
/// 插件开发者相关信息
/// </summary>
PluginInfo Information { get; }
/// <summary>
/// 插件帮助信息
/// </summary>
string ConfigHelpHtml { get; }
}
}
IHost.cs 文件:
{
/// <summary>
/// 这里只需提供常用的功能函数和配置信息即可
/// </summary>
public interface IHost
{
/// <summary>
/// 获取用户列表
/// </summary>
/// <returns></returns>
List<UserInfo> GetUsers();
/// <summary>
/// 获取插件提供者配置文件
/// </summary>
/// <param name="providerTypeName">插件类型名称</param>
/// <returns></returns>
string GetProviderConfiguration(string providerTypeName);
}
}
接下来我们开始设计针对文本格式化的插件接口,此接口将继承上面的基类接口(后继开始所有类型的插件都应该继承自 IProvider)。
由于要对当前输出内容进行格式化,所以还需让插件开发者知道当前的上下文环境,以方便调用所需的信息,同时如果想再灵活些的话,可添加一个格式化对象的枚举,指明当前插件只格式化哪部分信息,因此需先创建一个 FormattingContext 枚举和 ContextInfo 类以传递所需的信息,可在一个 ContextInfo.cs 文件里包含以上2个类:
ContextInfo.cs 文件:
{
public class ContextInfo
{
/// <summary>
/// 包含了所需的页面上下文信息
/// </summary>
public class ContextInfo
{
private FormattingContext context;
private HttpContext httpContext;
private string username;
/// <summary>
/// 初始化格式化对象所需的上下文对象
/// </summary>
/// <param name="context">格式化枚举对象</param>
/// <param name="httpContext">当前 http 上下文</param>
/// <param name="username">当前用户名</param>
public ContextInfo(FormattingContext context,
HttpContext httpContext, string username)
{
this.context = context;
this.httpContext = httpContext;
this.username = username;
}
/// <summary>
/// 获取当前格式化枚举对象
/// </summary>
public FormattingContext FormatContext
{
get { return context; }
}
/// <summary>
/// 获取当前 http 上下文
/// </summary>
public HttpContext HttpContext
{
get { return httpContext; }
}
/// <summary>
/// 获取当前用户名
/// </summary>
public string Username
{
get { return username; }
}
}
/// <summary>
/// 格式化枚举,指定需要格式化哪部分的信息
/// </summary>
public enum FormattingContext
{
/// <summary>
/// 侧栏
/// </summary>
Sidebar,
/// <summary>
/// 帖子标题
/// </summary>
PostTitle,
/// <summary>
/// 帖子内容
/// </summary>
PostContent,
/// <summary>
/// 页面头部
/// </summary>
PageHeader,
/// <summary>
/// 页面底部
/// </summary>
PageFooter,
/// <summary>
/// 未知
/// </summary>
Unknown
}
}
}
OK,现在我们可以创建 IFormatterProvider 接口了:
IFormatterProvider.cs 文件:
{
public interface IFormatterProvider : IProvider
{
/// <summary>
/// 对指定内容进行格式化
/// </summary>
/// <param name="content">需格式化的内容</param>
/// <param name="context">当前格式化对象上下文</param>
/// <returns>格式化后的内容</returns>
string Format(string content, ContextInfo context);
/// <summary>
/// 格式化帖子标题
/// </summary>
/// <param name="title">需格式化的帖子标题</param>
/// <param name="context">当前格式化对象上下文</param>
/// <returns>格式化后的内容</returns>
string FormatPostTitle(string title, ContextInfo context);
/// <summary>
/// 格式化页面头部内容
/// </summary>
/// <param name="head">需格式化的页面头部内容</param>
/// <param name="context">当前格式化对象上下文</param>
/// <returns>格式化后的内容</returns>
string FormatPageHead(string head, ContextInfo context);
/// <summary>
/// 格式化页面底部
/// </summary>
/// <param name="foot">需格式化的页面底部内容</param>
/// <param name="context">当前格式化对象上下文</param>
/// <returns>格式化后的内容</returns>
string FormatPageFoot(string foot, ContextInfo context);
/// <summary>
/// 格式化插件的执行优先级,此处很重要,因如多个格式化插件同时执行时,
/// 可能会覆盖其他的格式,所以要有个优先级问题
/// </summary>
int ExecutionPriority { get; }
}
}
此接口设计好后,以后所有与格式化相关的插件,都必须实现此接口,然后可根据插件作者需要实现不同的接口方法去格式化相应的内容。
OK,这篇就先讲到这吧,所需要的接口都定义好了,下一篇将详解如何实现相关的接口