2.2.2 分析松耦合的实现
上一节包含了很多细节,所以如果你一路上失去了对大局的关注,那也就不足为奇了。在本节中,我将尝试用更广泛的术语来解释所发生的事情。
相互作用
每一层中的类都以直接或抽象的形式相互作用。它们是跨越模块边界的,所以很难了解它们是如何相互作用的。 图2.20说明了依赖关系是如何被连接的。
当应用程序启动时,Global.asax的代码会创建一个新的自定义控制器工厂。 应用程序保持对控制器工厂的引用,所以当页面请求进来时,应用程序会在工厂上调用CreateController。工厂从web.config中查找连接字符串,并将其提供给SqlProductRepository的一个新实例。 它将SqlProductRepository实例注入HomeController的一个新实例中,并返回该实例。
然后,应用程序调用HomeController实例的Index方法,使其创建一个新的ProductService实例,在其构造函数中把SqlProductRepository实例传递给它。 ProductService在SqlProductRepository实例上调用GetFeaturedProducts方法。
最后,带有填充的FeatureProductsViewModel的ViewResult被返回,ASP.NET MVC框架找到并渲染了正确的页面。
依赖关系图
在第2.1.3节中,我们看到了依赖图如何帮助我们分析和理解架构实现所提供的灵活程度。DI是否改变了应用程序的依赖图?
图2.21显示,依赖关系图确实发生了变化。DomainModel不再有任何依赖关系,可以作为一个独立的模块。另一方面,数据访问库现在有一个依赖关系;在Mary的应用程序中,它没有依赖关系。
这应该提高我们的希望,即我们这次可以更有利地回答关于可支配性的原始问题。
- 我们可以用一个基于WPF的用户界面来取代基于Web的用户界面吗?这在以前是可能的,在新的设计中也是可能的。无论是Domain Model还是Data Access库都不依赖于基于Web的用户界面,所以我们可以很容易地用其他东西来代替它。
- 我们可以用一个能与Azure表服务一起工作的库来代替关系型数据访问库吗?在第三章中,我将描述应用程序如何定位和实例化正确的ProductRepository,所以现在请看以下内容:数据访问库是通过后期绑定加载的,并且类型名被定义为web.config中的应用程序设置。 我们可以扔掉当前的数据访问库,注入一个新的数据访问库,只要它也能提供ProductRepository的实现。
现在已经不可能孤立地使用当前的数据访问库了,因为它现在依赖于域模型。在许多类型的应用程序中,这不是一个问题,但如果利益相关者想要这个功能,我可以通过添加另一层间接性来解决问题:从Product中提取一个接口(例如,IProduct),并改变ProductRepository来与IProduct工作,而不是Product。这些抽象可以被移到一个单独的库中,由数据访问库和域模型共享。 这需要更多的工作,因为我需要在Product和IProduct之间编写代码,但这当然是可能的。
通过基于DI的设计,原来的Web应用可以逐渐转变为一个具有丰富的WPF界面和基于云的存储引擎的软件+服务应用。最初的努力只剩下DomainModel了,但这是很恰当的,因为它封装了所有重要的业务规则,因此,我们应该期待它成为最重要的模块。
当我们开发应用程序时,我们不可能预见到产品未来可能需要的每一个方向,但只要我们能保持我们的选择是开放的,那就没有问题。DI帮助我们建立松耦合的应用程序,这样我们就可以根据需要重复使用或替换不同的模块。