zoukankan      html  css  js  c++  java
  • 一步步实现自己的框架系列(二):插件框架实现

      不好意思各位同学,本系列文章更新比较慢,因为我也要工作,况且还需要抽出时间编码验证理论,当然找借口总归是不好的,我们都是人,需要休息与娱乐嘛。

      其实.net平台已经有自己的插件框架,比如MEF,MAF这些都是.net自带的框架,前者注重灵活,后者注重物理隔离。不过这不是今天的重点,今天的重点是做我们自己的框架。

      第一步:插件模型设计

      既然是插件框架就会有插件,就会有放插件的地方,我们就需要设计插件容器,这样既可以灵活的管理插件,也使代码的层次结构更加清晰,图示紫色部分是插件与插件容器部分,外边蓝色的就是我们需要使用插件的拥有者,我发现一张图片的效果远比一堆庸俗的文字效果来的更直接,所以如果能用图表达的地方我尽量用图去表达。

      第二步:接口设计

        既然是插件我们如何标识我们的插件呢,看看MEF的导出设计,

    [Export]

    public class Part

    {

    }

    这里我们借来用下,没错,就是使用特性来标识我们导出的插件,既然这种设计这么优秀我们为什么不用呢?

    单部件特性设计

    namespace GL.Core
    {
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
        public class SinglePartAttribute : Attribute
        {
    
        }
    }

    多部件特性设计

    namespace GL.Core
    {
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
        public class MultiplePartAttribute : Attribute
        {
    
        }
    }

      为什么会设计两个特性呢?我们在使用一些功能时,比如设备管理,同一个客户端实例只需要一个设备管理的插件就够了,尽管我们可以写很多,但同一时刻我们只需要一个实例去管理就可以了,那多部件呢,比如我们客户端是一个图片效果处理程序,同一时刻,我们既可以使用图片灰度功能,也可以使用图片锐化功能,也可以使用负片功能等等,那么这种设计是合理的。
      既然是插件,那么插件的描述信息必不可少,那么怎么设计我们的插件描述信息呢——特性,这里也使用特性,

    比如这样定义我们的插件,我们既可以知道这是一个插件,又能获取插件的描述信息,简洁明了。

    [Single]
    [PartMetadata("Name","PartManage")]
    [PartMetadata("Author","GL")]
    public class PartManage
    {
    
    }

    插件元数据描述

    namespace GL.Core
    {
        public class PartMetadata : Attribute
        {
            public string Name { get; private set; }
    
            public object Value { get; private set; }
    
            public PartMetadata(string name, object value)
            {
                this.Name = name;
                this.Value = value;
            }
        }
    }

      为了方便在内存中操作我们的插件,为插件设计一个信息描述实体是必须的,当然,根据实际需要大家可以自己添加或删除一些描述信息,为方便扩展插件描述这里给出了描述的接口。

    namespace GL.Core
    {
        public interface IPartInformation
    {
    /// <summary> /// 插件名称 /// </summary> string PartName { get; } /// <summary> /// 插件版本 /// </summary> string PartVersion { get; } /// <summary> /// 插件描述 /// </summary> [DefaultValue("")] string PartDescription { get; } /// <summary> /// 插件类型 /// </summary> Type PartType { get; } /// <summary> /// 依赖的插件类型 /// </summary> [DefaultValue(null)] Type[] PartDependencies { get; } /// <summary> /// 插件作者 /// </summary> [DefaultValue("")] string PartAuthor { get; } /// <summary> /// 最后修改日期 /// </summary> [DefaultValue("")] string LastModifiedDate { get; } /// <summary> /// 备注 /// </summary> [DefaultValue("")] string Comment { get; } } }

      有时候某些插件我们不希望启用,或者多插件版本切换的时候,我又不想每次都替换那些Dll,毕竟那是些麻烦又无聊的事情,这时候我就的给我们的每一个插件添加一个配置项信息了,想启用什么或者禁用什么改改配置文件就好了(当然,这工作交给小弟就好了,或者弄个配置文件修改的工具,随便一个人就能切换了)。
    没想到还能为插件配置什么,就一个是否启用

    namespace GL.Core
    {
        public class IPartConfigration
        {
            public string PartType { get; set; }
            public bool IsEnable { get; set; }
    
            public IPartConfigration() { }
            
            public IPartConfigration(string partType, bool isEnable)
            {
                this.PartType = partType;
                this.IsEnable = isEnable;
            }
        }
    }

      跟插件相关的就差不多了,下面来设计下我们的插件容器吧,所谓容器就是能放东西的东西,我感觉上句话包括这句其实是废话,呵呵。既然是插件容器就得能放插件,没有接口设计的类,让人理解起来总是那么费劲,首先来看下我们插件容器接口的设计:

    namespace GL.Core
    {
        public interface IGLPartContainer
        {
            /// <summary>
            /// 单实例插件
            /// </summary>
            IList<Type> SingleParts { get; }
    
            /// <summary>
            /// 多实例插件
            /// </summary>
            IList<Type> MultipleParts { get; }
    
            /// <summary>
            /// 获取所有插件实例与配置信息
            /// </summary>
            IDictionary<object, IPartInformation> ActivePart { get; }
    
            /// <summary>
            /// 添加插件
            /// </summary>
            /// <param name="partType"></param>
            /// <param name="partConfigration"></param>
            void AddPart(Type partType, IPartConfigration partConfigration);
    
            /// <summary>
            /// 移除指定实例插件
            /// </summary>
            /// <param name="partInstance"></param>
            void RemovePart(object partInstance);
    
            /// <summary>
            /// 获取单实例插件
            /// </summary>
            /// <param name="partType"></param>
            /// <returns></returns>
            object GetSinglePart(Type partType);
    
            /// <summary>
            /// 获取多实例插件
            /// </summary>
            /// <param name="partType"></param>
            /// <returns></returns>
            IEnumerable<object> GetMultipleParts(Type partType);
    
            /// <summary>
            /// 获取插件信息
            /// </summary>
            /// <param name="partType"></param>
            /// <returns></returns>
            IPartInformation GetPartInformation(object instance);
    
            /// <summary>
            /// 获取指定类型插件是否启用
            /// </summary>
            /// <param name="partType"></param>
            /// <returns></returns>
            bool IsPartEnabled(Type partType);
    
        }
    }

    第三步:类的实现

      接着就是类的实现,里面会涉及到反射的应用,linq等基本应用

    namespace GL.Core
    {
        public sealed class GLPartContainer<TOwner> : IGLPartContainer
            where TOwner : class
        {
    
            private static readonly ILog _logger = LogManager.GetLogger("GL.Core.GLPartContainer");
    
            /// <summary>
            /// 插件实例注册
            /// </summary>
            private IDictionary<object, IPartInformation> _PartInstanceRegistry = new Dictionary<object, IPartInformation>();
    
            /// <summary>
            /// 插件配置
            /// </summary>
            private IDictionary<string, IPartConfigration> _PartConfig = new Dictionary<string, IPartConfigration>();
            
    
            public IList<Type> SingleParts { get; private set; }
    
            public IList<Type> MultipleParts { get; private set; }
    
            public IDictionary<object, IPartInformation> ActivePart 
            {
                get { return _PartInstanceRegistry; }
            }
    
            /// <summary>
            /// 容器拥有者
            /// </summary>
            public TOwner Parent { get; private set; }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="owner">容器拥有者</param>
            /// <param name="partConfigFile">插件配置文件目录</param>
            /// <param name="partDirectories">插件目录(绝对路径)</param>
            public GLPartContainer(TOwner owner, string partConfigFile, string[] partDirectories)
            {
                this.Parent = owner;
    
                if (File.Exists(partConfigFile))
                {
                    var doc = XDocument.Load(partConfigFile);
                    _PartConfig = doc.Descendants("part").ToDictionary(
                            e => e.Attribute("type").Value,
                            e => new IPartConfigration(e.Attribute("type").Value, bool.Parse(e.Attribute("isEnabled").Value)));
                }
    
                foreach (var path in partDirectories)
                {
                    if (!Directory.Exists(path))
                    {
                        throw new GLException(GLErrorCodes.PartDirectoryNotExists, string.Format("Part Directory Not Exist:{0}", path));
                    }
                    string[] dllPaths = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories);
                    try
                    {
    
                        var typeList = from dll in dllPaths
                                       from type in Assembly.LoadFile(dll).GetTypes()
                                       select type;
    
                        SingleParts = (from type in typeList
                                      from attribute in type.GetCustomAttributes(false)
                                      where attribute.GetType().Equals(typeof(SinglePartAttribute))
                                      select type).ToList();
    
                        MultipleParts = (from type in typeList
                                        from attribute in type.GetCustomAttributes(false)
                                        where attribute.GetType().Equals(typeof(MultiplePartAttribute))
                                        select type).ToList();
                    }
                    catch (Exception e)
                    {
                        throw new GLException(GLErrorCodes.PartLoadError, "Platform Load Type Error:{0}", e.Message, e);
                    }
                }
    
                if (SingleParts == null)
                {
                    SingleParts = new List<Type>();
                }
                if (MultipleParts == null)
                {
                    MultipleParts = new List<Type>();
                }
    
                foreach (var part in SingleParts)
                {
                    var instance = Activator.CreateInstance(part);
                    _PartInstanceRegistry.Add(instance, GetPartInformation(part));
                }
    
                foreach (var part in MultipleParts)
                {
                    var instance = Activator.CreateInstance(part);
                    _PartInstanceRegistry.Add(instance, GetPartInformation(part));
                }
            }
    
            public void AddPart(Type partType, IPartConfigration partConfigration)
            {
                var singlePart = from attribute in partType.GetCustomAttributes(false)
                              where attribute.GetType().Equals(typeof(SinglePartAttribute))
                              select partType;
    
                if (singlePart != null)
                {
                    var instance = Activator.CreateInstance(partType);
                    _PartInstanceRegistry.Add(instance, GetPartInformation(partType));
                    SingleParts.Add(partType);
                }
                else
                {
                    var multiplePart = from attribute in partType.GetCustomAttributes(false)
                                       where attribute.GetType().Equals(typeof(MultiplePartAttribute))
                                       select partType;
                    if (multiplePart != null)
                    {
                        var instance = Activator.CreateInstance(partType);
                        _PartInstanceRegistry.Add(instance, GetPartInformation(partType));
                        MultipleParts.Add(partType);
                    }
                    else
                    {
                        throw new GLException(GLErrorCodes.PartNotFound, "指定类型中未发现插件");
                    }
                }
            }
    
            public IPartInformation GetPartInformation(object instance)
            {
                IPartInformation partInformation = null;
                _PartInstanceRegistry.TryGetValue(instance, out partInformation);
                return partInformation;
            }
    
            private IPartInformation GetPartInformation(Type type)
            {
                var partMetadatas = type.GetCustomAttributes(false).Where(e => e.GetType().Equals(typeof(PartMetadataAttribute)));
                var partInformation = new PartInformation();
                foreach (var metadata in partMetadatas)
                {
                    var partMetadata = metadata as PartMetadataAttribute;
                    partInformation.SetProperty(partMetadata.Name, partMetadata.Value);
                }
    
                return partInformation;
            }
    
            public bool IsPartEnabled(Type partType)
            {
                return _PartConfig.ContainsKey(partType.FullName) ? _PartConfig[partType.FullName].IsEnable : false;
            }
    
    
            public void RemovePart(object partInstance)
            {
                if (_PartInstanceRegistry.ContainsKey(partInstance))
                {
                    _PartInstanceRegistry.Remove(partInstance);
                }
                else
                {
                    throw new GLException(GLErrorCodes.PartNotFound, string.Format("instance of {0} not found", partInstance.GetType().FullName));
                }
            }
    
            public object GetSinglePart(Type partType)
            {
                var instanceCount = _PartInstanceRegistry.Keys.Count(p => partType.IsAssignableFrom(p.GetType()));
                if (instanceCount > 1)
                {
                    throw new GLException(GLErrorCodes.PartMultipleInstances, string.Format("{0} has multiple instances", partType.FullName));
                }
                return _PartInstanceRegistry.Keys.SingleOrDefault(p => partType.IsAssignableFrom(p.GetType()));
            }
    
    
            public IEnumerable<object> GetMultipleParts(Type partType)
            {
                return _PartInstanceRegistry.Keys.Where(p => partType.IsAssignableFrom(p.GetType()));
            }
    
        }
    }
    View Code

    第四步:单元测试

      好了,我们插件存放的容器就设计好了,来个单元测试,接下来享受下我们的成果吧:

     class Program
        {
            static void Main(string[] args)
            {
                Program p = new Program();
                var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"PartConfig.config") ;
                IGLPartContainer cont = new GLPartContainer<Program>(p, configPath, new string[] { AppDomain.CurrentDomain.BaseDirectory });
                var p1 = cont.GetSinglePart(typeof(Part1));
    
                Console.WriteLine("Part1 Full Name : {0}",p1.GetType().FullName);
    
                var xxx = cont.GetPartInformation(p1);
    
                Console.WriteLine("Part1 Metadata : {0}", xxx.PartName, xxx.PartType);
    
                var p2 = cont.GetMultipleParts(typeof(Part2));
    
                Console.WriteLine("Part12 Full Name : {0}", p1.GetType().FullName);
    
                Console.ReadLine();
            }
        }

     运行结果:

    既然来了,何不留下您的脚印,如果您觉得本文对你有所帮助的话,请点击推荐,如果你想关注本系列文章的话,请点击关注我。

  • 相关阅读:
    Cassandra key说明——Cassandra 整体数据可以理解成一个巨大的嵌套的Map Map<RowKey, SortedMap<ColumnKey, ColumnValue>>
    Cassandra二级索引原理——新创建了一张表格,同时将原始表格之中的索引字段作为新索引表的Primary Key,并且存储的值为原始数据的Primary Key,然后再通过pk一级索引找到真正的值
    Cassandra 的数据存储结构——本质是SortedMap<RowKey, SortedMap<ColumnKey, ColumnValue>>
    Cassandra 单机入门例子——有索引
    cassandra框架模型之二——存储机制 CommitLog MemTable SSTable
    cassandra框架模型之一——Colum排序,分区策略 Token,Partitioner bloom-filter,HASH
    elasticsearch负载均衡节点——客户端节点 node.master: false node.data: false 其他配置和master 数据节点一样
    Elasticsearch压缩索引——lucene倒排索引本质是列存储+使用嵌套文档可以大幅度提高压缩率
    elasticsearch 2.2+ index.codec: best_compression启用压缩
    一些开源搜索引擎实现——倒排使用原始文件,列存储Hbase,KV store如levelDB、mongoDB、redis,以及SQL的,如sqlite或者xxSQL
  • 原文地址:https://www.cnblogs.com/guanglin/p/3093283.html
Copyright © 2011-2022 走看看