Pro ASP.NET MVC 3 Framework学习笔记之五
一,创建松耦合的组件
1."分解关注点"是MVC模式里面一个非常重要的特性。我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系。理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改。通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且将这个接口命名为IEmailSender。任何其他的应用程序的组件需要引用IEmailSender里面的方法就行了。比如有一个重置密码的组件PasswordResetHelper需要在用户重置密码后发生邮件,下图展示这它们之间的关系:
通过引入IEmailSender,我们就能够确保在PasswordResetHelper跟MyEmailSender之间没有直接的依赖关系。比如,我们可以用其他的实现了发送邮件的Provider来替换当前的MyEmailSender而不会对PasswordResetHelper造成影响,从这里也能够体会到松耦合的好处吧。
当然并不是所有的组件之间存在的关系都需要用接口来解耦和。这取决于我们的应用程序的复杂程度,需要什么样的测试,长期维护的可能性。我们不用去对一个简单的ASP.NET MVC程序执行解耦。
2.使用依赖注入
接口能够帮助我们解耦组件,但是这样仍然面临一个问题,那就是C#并没有提供一种嵌入的方式来比较容易的创建实现接口的对象,因为我们只能创建一个具体实现了接口的组件的实例,比如这里的的MyEmailSender的实例。像下面的这种方式:
public class PasswordResetHelper
{
public void ResetPassword()
{
IEmailSender mySender = new MyEmailSender();
//一些关于邮件的详细
mySender.SendEmail();
}
}
我们仅仅做了松耦合的一部分工作,PasswordResetHelper类通过IEmailSender来配置和发送邮件,通过接口的实现来创建对象,这里需要创建一个MyEmailSender的实例。这样看来,我们可能让事情更糟,因为现在的PasswordResetHelper同时依赖IEmailSender和MyEmailSender,正如下图所显示的那样:
其实我现在需要一种方式来获取对象(这里就是指上面代码里的mySender),这个对象是实现了我们给定的接口但不是直接去创建实现接口(这里指MyEmailSender)对象本身。对于这个问题的解决方案,我们称为依赖注入(dependency injection(DI)),也可以被认为是控制反转(inversion of control(IoC))。
3.DI(dependency injection):是一种完成松耦合的设计模式,这是一个非常重要的概念,它是高效MVC开发的中心。
DI分为两个部分:一是从我们的组件里面移除任何的对具体类的依赖性。在我们的这个例子里面,我们这样来做,将对必要接口的实现移动到类的构造器里面,如下所示:
public class PasswordResetHelper
{
private IEmailSender emailSender;
public PasswordResetHelper(IEmailSender emailSenderParam)
{
emailSender = emailSenderParam;
}
public void ResetPassword()
{
//邮件的详细配置...
emailSender.SendEmail();
}
}
www.zzzyk.com
这样做了以后,我们可以发现,没有了MyEmailSender,这也就意味着我们打破了PasswordResetHelper和MyEmailSender之间的依赖性。PasswordResetHelper的构造器需要一个对象作参数,而这个对象是IEmailSender接口的的实现,它不用去知道这个对象是什么,或者说它根本不用去关心,并且也不用负责去创建它。
4.通过上面的操作,依赖在运行时就被注入到了PasswordResetHelper里面,也意味着那些实现了IEmailSender接口的类的实例将会被创建,并在PasswordResetHelper实例化期间传递给它的构造器。这样在PasswordResetHelper和任何实现了它需要的接口的类之间没有编译时的依赖。
PasswordResetHelper是通过它的构造器来实现依赖注入的,我们把这种称为Constructor Injection(构造器注入);我也可以通过它的公共属性来实现依赖注入,通常称这种方式为Setter Injection(设置注入)。如果你想了解Constructor Injection vs. Setter Injection,可以猛击这里;
因为依赖是在运行时处理,这样我们就可以决定在程序运行的时候使用哪个接口实现。就像这里我们可以不同的Email Provider,或者是伪造一个仅仅用来测试。通过上面的方式我们目的就达到了。
二,一个具体的MVC 依赖注入的例子
还是回到我之前做的竞拍,接下就是将依赖注入应用到我们的竞拍程序里面,我们的目标很简单,创建一个controller命名为AdminController,我们使用MembersRepository来持久化,为了解决AdminController和MembersRepository之间的耦合,我们定义一个接口IMembersRepository.具体的代码如下:
public inte易做图ce IMembersRepository
{
void AddMember(Member member);
Member FetchByLoginName(string loginName);
void SubmitChanges();
}
public class MembersRepository : IMembersRepository
{
public void AddMember(Member member) {...}
public Member FetchByLoginName(string loginName) {... }
public void SubmitChanges() {...}
}
现在我写个controller类,它依赖于IMembersRepository,如下所示:
public class AdminController : Controller
{
IMembersRepository membersRepository;
public AdminController(IMembersRepository repositoryParam)
{
membersRepository = repositoryParam;
}
public ActionResult ChangeLoginName(string oldLoginParam, string newLoginParam)
{
Member member = membersRepository.FetchByLoginName(oldLoginParam);
member.LoginName = newLoginParam;
membersRepository.SubmitChanges();
//用来呈现一些View
}
}
AdminController需要一个IMembersRepository接口的实现的对象作为构造器的参数。这个在运行时会被注入,也会允许AdminController对接口实现的对象的实例进行操作而不会发生耦合。
使用一个依赖注入容器
我已经解决了依赖的问题,因为我们会在程序运行时
补充:Web开发 , ASP.Net ,