zoukankan      html  css  js  c++  java
  • Ninject之旅之八:Ninject插件模型(附程序下载)

    摘要

    在前面的章节中,我们看了在单一的绑定条件下Ninject能够处理依赖类型,就是说,每个服务类型只绑定到单一的实现类型。然而,有些情况下我们需要绑定一个抽象服务类型到多个实现,这叫多个绑定。多个绑定有两种情况。第一个是插件模型实现,另一个是上下文绑定。这篇文章介绍插件模型实现,下一篇文章介绍上下文绑定。

    附:程序下载

    插件模型让一个应用程序获得很强的可扩展性而不用修改源代码。下面的例子,我们将实现一个音乐播放器应用程序,使用解码插件来支持不同的音乐格式。这个应用程序使用两个内置的解码器,也可以添加更多的解码器来扩展我们播放器支持的格式。请注意为了让应用程序尽可能简单,许多复杂的细节将不被实现。

    先定义一个解码器接口:

    1     public interface ICodec
    2     {
    3         string Name { get; }
    4         bool CanDecode(string extension);
    5         Stream Decode(Stream inStream);
    6     }

    添加两个解码器类实现解码器接口:

    Mp3:

     1     public class Mp3Codec : ICodec
     2     {
     3         public string Name
     4         {
     5             get
     6             {
     7                 return "MP3 Audio";
     8             }
     9         }
    10 
    11         public bool CanDecode(string extension)
    12         {
    13             return extension == ".mp3";
    14         }
    15 
    16         public Stream Decode(Stream inStream)
    17         {
    18             //some decode logic added here
    19             return null;
    20         }
    21     }

    Wma:

     1 public class WmaCodec : ICodec
     2     {
     3         public string Name
     4         {
     5             get
     6             {
     7                 return "Windows Media Auido";
     8             }
     9         }
    10 
    11         public bool CanDecode(string extension)
    12         {
    13             return extension == ".wma";
    14         }
    15 
    16         public Stream Decode(Stream inStream)
    17         {
    18             //some decode logic added here
    19             return null;
    20         }
    21     }

    下一步是实现我们可插拔的播放器类。让播放器可扩展的是他依赖一系列的ICodec对象,而不是某些具体的解码器:

    1 public class Player
    2 {
    3     private readonly ICodec[] codecs;
    4     // Note that the constructor parameter is not a single ICodec.
    5     public Player(IEnumerable<ICodec> codecs)
    6     {
    7         this.codecs = codecs.ToArray();
    8     }
    9 }

    然后添加一个Play方法到播放器类:

    1 public void Play(FileInfo fileInfo)
    2 {
    3     ICodec supportingCodec = FindCodec(fileInfo.Extension);
    4     using (var rawStream = fileInfo.OpenRead())
    5     {
    6         var decodedStream = supportingCodec.Decode(rawStream);
    7         PlayStream(decodedStream);
    8     }
    9 }

    这个方法接收一个FileInfo对象,查找合适的解码器后,解码播放这个文件。

    实现FindCodec方法:

    1 private ICodec FindCodec(string extension)
    2 {
    3     foreach (ICodec codec in codecs)
    4         if (codec.CanDecode(extension))
    5             return codec;
    6     throw new Exception("File type not supported.");
    7 }

    FindCodec调用每个codec对象的CanDecode方法来找到支持这个文件扩展名的解码器。如果找不到任何合适的解码器,将抛出一个异常。我们需要记住的一件事是没有具体的解码器在foreach循环之前被解析。

    最后在Main方法里添加下面的代码:

    1 using (var kernel = new StandardKernel())
    2 {
    3     kernel.Bind(b => b.FromAssembliesMatching("*")
    4         .SelectAllClasses()
    5         .InheritedFrom<ICodec>()
    6         .BindAllInterfaces());
    7 }        

    前面的约定指示Ninject自动注册所有的ICodec接口的实现而不是为他们分别每个都声明绑定。

    因为ICodec类型被绑定到多个实现,他只能被解析成一系列的对象而不是单一的对象。因此,使用下面的构造函数解析ICodec将导致一个运行时异常:

    1 public Consumer(ICodec codec){}

    下面的代码也将产生运行时异常:

    1 ICodec codec = Kernel.Get<ICodec>();

    上面两行代码,Ninject将试着解析ICodec接口,但是它发现多于一个的具体的实现类型。代替使用Get<T>,我们可以调用GetAll<T>方法来得到ICodec的所有实现。下面的代码显示所有的支持的codec:

    1 IEnumerable<ICodec> codecs = kernel.GetAll<ICodec>();
    2 foreach (ICodec codec in codecs)
    3 System.Console.WriteLine(codec.Name);

    现在,任何的在应用程序的根路径下,有ICodec实现的程序集都将被我们的播放器应用程序认为是一个codec插件。我们的应用程序甚至不需要添加对codec工程的引用。

    下面是完整的播放器类代码:

     1     public class Player
     2     {
     3         private readonly ICodec[] _codecs;
     4 
     5         // Note that the constructor parameter is not a single ICodec.
     6         public Player(IEnumerable<ICodec> codecs)
     7         {
     8             this._codecs = codecs.ToArray();
     9         }
    10 
    11         public void Play(FileInfo fileInfo)
    12         {
    13             ICodec supportingCodec = FindCodec(fileInfo.Extension);
    14             using (var rawStream = fileInfo.OpenRead())
    15             {
    16                 var decodedStream = supportingCodec.Decode(rawStream);
    17                 PlayStream(decodedStream);
    18             }
    19         }
    20 
    21         private void PlayStream(Stream decodedStream)
    22         {
    23             return;
    24         }
    25 
    26         private ICodec FindCodec(string extension)
    27         {
    28             foreach (ICodec codec in _codecs)
    29             {
    30                 if (codec.CanDecode(extension))
    31                 {
    32                     return codec;
    33                 }
    34             }
    35             throw new Exception("File type not supported.");
    36         }
    37     }
  • 相关阅读:
    Eclipse 项目导入 Android Studio 导致的乱码问题
    Android 系统服务
    Android 系统内核层与 Linux Kernel 的比较
    DPI 计算及速查表
    Android 引用文件(.db)的三种方式
    阅读记录(2017年1月)
    如何让电脑自动记录每次开关机时间
    使用VS2010编译Qt 5.6.1过程记录
    Windows无线网“无法连接到这个网络”的解决办法
    怎样在Windows资源管理器中添加右键菜单以及修改右键菜单顺序
  • 原文地址:https://www.cnblogs.com/uncle_danny/p/6071647.html
Copyright © 2011-2022 走看看