现实生活中,是具体的播放器和具体的媒体文件没有关系,你给它一个 Mp3 文件他可以播放,给它一个 Mp4 文件它也可以播放,你删掉你的媒体文件,播放器照样在,具体什么播放器,播放什么文件,控制权全部是我们用户自己。
上面的示例中基本实现了隔离,具体的播放器跟具体的媒体隔离了,具体的播放器只跟媒体接口和播放器接口有关。但是 PlayMedia 的方法里面的具体对象,写死了,控制权非常小,如果我想用百度影音播放呢,我想换一首音乐呢,只能重新改代码,那控制怎么进行转移呢?
我们可以通过反射来创建,把具体的文件名写在配置文件里,这时候客户端代码也不用变了,只需要改配置文件就好了,稳定性又有了提高,如下:
- public void PlayMedia()
- {
- IMediaFile _mtype = Assembly.Load(ConfigurationManager.AppSettings["AssemName"]).CreateInstance(ConfigurationManager.AppSettings["MediaName"]);
- IPlayer _player = Assembly.Load(ConfigurationManager.AppSettings["AssemName"]).CreateInstance(ConfigurationManager.AppSettings["PlayerName"]);
- _player.Play(_mtype);
- }
这个具对象是哪一个,全由配置文件来控制了,这个具体对象的控制权交给了配置文件了,这也是人们常说的控制反转。
控制反转 IoC 是 Inversion of Control 的缩写,是说对象的控制权进行转移,转移到第三方,比如转移交给了 IoC 容器,它就是一个创建工厂,你要什么对象,它就给你什么对象,有了 IoC 容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖 IoC 容器了,通过 IoC 容器来建立它们之间的关系。
上面说到控制反转,是一个思想概念,但是也要具体实现的,上面的配置文件也是一种实现方式。依赖注入提出了具体的思想。
依赖注入 DI 是 Dependency Injection 缩写,它提出了 "哪些东东的控制权被反转了,被转移了?",它也给出了答案:"依赖对象的创建获得被反转"。
所谓依赖注入,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中。
上面的示例中,哪些要依赖注入,依赖对象需要获得实例的地方,即 PlayMedia 方法,需要 IPlayer 具体对象和 IMediaFile 的具体对象,找到了地方就从这里下手,为了灵活的控制这两个对象,必须是外面能够控制着两个对象的实例化,提供对外的操作是必要的,可以是属性,可以是方法,可以是构造函数,总之别的地方可以控制它,下面将会使用 Unity 来注入,使用的是构造函数注入,代码如下:
- /// <summary>
- /// 用户播放媒体文件
- /// </summary>
- public class OperationMain
- {
- IMediaFile _mtype;
- IPlayer _player;
- public OperationMain(IPlayer player, IMediaFile mtype)
- {
- _player = player;
- _mtype = mtype;
- }
- public void PlayMedia()
- {
- _player.Play(_mtype);
- }
- }
- /// <summary>
- /// 播放器
- /// </summary>
- public interface IPlayer
- {
- void Play(IMediaFile file);
- }
- /// <summary>
- /// 默认播放器
- /// </summary>
- public class Player : IPlayer
- {
- public void Play(IMediaFile file)
- {
- Console.WriteLine(file.FilePath);
- }
- }
- /// <summary>
- /// 媒体文件
- /// </summary>
- public interface IMediaFile
- {
- string FilePath { get; set; }
- }
- /// <summary>
- /// 默认媒体文件
- /// </summary>
- public class MediaFile : IMediaFile
- {
- public string FilePath { get; set; }
- }
给 OperationMain 类一个构造函数,因为 Unity 有一个构造函数注入,调用代码如下:
- static UnityContainer container = new UnityContainer();
- static void init()
- {
- container.RegisterType<IPlayer, Player>();
- container.RegisterType<IMediaFile, MediaFile>();
- }
- static void Main(string[] args)
- {
- init();
- OperationMain op1 = container.Resolve<OperationMain>();
- op1.PlayMedia();
- OperationMain op3 = container.Resolve<OperationMain>();
- op3.PlayMedia();
- //普通方式
- OperationMain op2 = new OperationMain(new Player(), new MediaFile());
- op2.PlayMedia();
- Console.Read();
- }
看出来吧,Unity 的功能远不止这些,你可以初始化时注册 N 多,以后直接使用,而不用使用 new,还有实例周期的控制、配置文件等灵活控制,具体可以看看 Unity(具体不是本节的范畴)的说明。
通过一个小例子由浅入深地进行优化,已加深对 IoC 模式的理解,我想复杂的结构也是从这种简单的架构累加起来的。
最近在看相关文章,很多都太专业化了没怎么看懂,这是自己现在对 IoC 的一些理解,记录下来,要不然时间一久,也就忘了。
自己对 IoC 模式理解还很浅,希望得到各位的指点。
来源: