本次的笔记分为三个部分:Ninject(依赖注入容器,前面有介绍的,如果你第一次路过这里,可以先看下我前面的笔记),NUnit(单元测试工具),Moq(用来模拟在单元测试中的接口实现).今天我做的笔记是关于第一部分:Ninject.
如果你对依赖注入(DI)没有任何的了解,你可以看看我前面的笔记或者在网上搜索相关的资料进行了解。
下面通过一个实例来介绍Ninject的使用,首先我们需要猛击这里下载相关的DLL。我们仍然用到的前面的Product,实现技术所有Product的总价值。下面通过几个步骤来具体的介绍:
1.创建一个Console Application,如果你不介意,我们暂且给它命名为NinjectDemo.接下来我们创建一个Product类,如下所示:
namespace NinjectDemo{public class Product {public int ProductID { get; set; }public string Name { get; set; }public string Description { get; set; }public decimal Price { get; set; }public string Category { set; get; } }}
接着创建一个接口IValueCalculator,如下所示:
namespace NinjectDemo{public interface IValueCalculator {decimal ValueProducts(params Product[] products); }}
接着创建一个接口实现,如下所示:
namespace NinjectDemo{public class LinqValueCalculator:IValueCalculator {public decimal ValueProducts(params Product[] products) {return products.Sum(p => p.Price); } }}
这里我们计算Products的总价用了LINQ的扩展方法(关于扩展方法在前面的笔记有介绍),当然这里你完全可以自己遍历Products求和并返回一个Decimal类型的值。下面我们创建一个类用来实现依赖注入。ShoppingCart.cs如下所示:
public class ShoppingCart {private IValueCalculator calculator;public ShoppingCart(IValueCalculator calcParam) { calculator = calcParam; } public decimal CalculateStockValue() { Product[] products = { new Product() { Name = "Kayak", Price = 275M}, new Product() { Name = "Lifejacket", Price = 48.95M},new Product() { Name = "Soccer ball", Price = 19.50M},new Product() { Name = "Stadium", Price = 79500M} };decimal totalValue = calculator.ValueProducts(products);return totalValue; } }
2.这里用到的依赖注入就是我们前面笔记里面的介绍的一种Constructor Injection(构造器注入),通过接口实现的实例作为参数传递给ShoppingCart的构造器来实现ShoppingCart与LinqValueCalculator(IValueCalculator接口实现)的解耦。下面的图很好的阐释了这几个类或接口直接的关系,如下所示:
呵呵,其实解耦的操作,按我自己的理解就是达到在两个耦合的对象之间引入一个"第三者",这样我们目的就达到了。当然在程序里面我常常需要这样的"第三者",现实的生活可不要这样,呵呵。这里的比方不是很恰当哈,大家就不要深究了。这里的ShoppingCart与LinqValueCalculator都依赖于IValueCalculator,但是彼此之间并没有直接的联系,甚至不知道彼此的存在。我可以改变LinqValueCalculator的实现,或者干脆用其他的方式来实现IValueCalculator接口,而不会让ShoppingCart有任何察觉,因为现在的它已经不在那么聪明了,呵呵。
3.从上面的几个类和接口之间的关系图我们可以发现,ShoppingCart,IValueCalculator,LinqValueCalculator这三个类型都跟Product有直接的联系。我们并不用担心,因为这里的Product等效于Domain Model Type(领域模型类型),并且我期望这样的类跟应用程序的其他部分发生强耦合。当然如果我们不是为了创建MVC程序,我们可能会有不同的看法,或许也会为Product采取解耦操作。
4.我们的目标就是要创建ShoppingCart的实例并且以IValueCalculator实现作为构造器参数的方式来注入。这正好是Ninject(DI容器)为我们充当的角色,在我们使用Ninject之前,我先引入Ninject.dll,并且查看一些我们项目的属性确定一下我们的Target Framework是. NET Framework 4,如果是 .NET Framework 4 Client Profile,在后面编译时会报错提示没有Ninject命名空间,原因是Client Profile会忽略Ninject.dll。
5.关于Ninject的使用,我们可以通过下面的代码(Console Application的Program.cs).如下所示:
static void Main(string[] args) { IKernel ninjectKernel = new StandardKernel(); ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();//获取接口实现 IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>(); //创建ShoppingCart的实例并注入依赖 ShoppingCart cart = new ShoppingCart(calcImpl); //执行 Console.WriteLine("Tatol:{0:c}", cart.CalculateStockValue()); }
接下来我们对加粗的代码进行介绍,使用Ninject,我们首先创建一个IKernel对象,通过这个对象来与Ninject交互。
一旦我们创建了IKernel对象后,接下需要通过Ninject做两个操作。
第一,绑定我们要创建的接口类型,在这里就是要告诉Ninject,当收到接口实现的请求时,为我们创建一个LinqValueCalculator的实例并返回给我们。这里我们使用的是在IKernel里面定义的Bind<T>()...To<T>()方法。正如代码:ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();这样我们就完成了注册工作了(前面相关的笔记有提到DI容器的使用)。
第二,使用Ninject的Get()方法创建接口实现的对象,这也就是我需要的作为参数传递给ShoppingCart构造器的,还记得吧!嘿嘿!
Ok,我们再来梳理一些刚才的思路,首先我们是解耦,并且通过将IValueCalculator的实现的对象作为参数传递给ShoppingCart构造的方式实现了;
接下来,我们通过DI容器来管理和获取这个参数,而我们又是通过Ninject来实现的。到这里我们对Ninject应该有了一些了解了吧。
好了,今天的笔记就到这里。明天会对Ninject的用法进一步学习。
初学MVC,加上跛脚的英语,笔记里面肯定有理解不准确和错误的地方,请路过的朋友们多多帮助!谢谢!
晚安!