演进式例解控制反转(IoC)、依赖注入(DI)之一

前端之家收集整理的这篇文章主要介绍了演进式例解控制反转(IoC)、依赖注入(DI)之一前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原文地址:http://haolloyin.blog.51cto.com/1177454/458416/


近来总是接触到IoCInversion of Control,控制反转)、DIDependency Injection,依赖注入)等编程原则或者模式,而这些是著名Java框架SpringStruts等的核心所在。针对此查了Wikipedia中各个条目,并从图书馆借来相关书籍,阅读后有些理解,现结合书中的讲解以及自己的加工整理如下:

问题描述:

开发一个能够按照不同要求生成ExcelPDF格式的报表的系统,例如日报表、月报表等等。

解决方案:

根据“面向接口编程”的原则,应该分离接口与实现,即将生成报表的功能提取为一个通用接口ReportGenerator,并提供生成ExcelPDF格式报表的两个实现类ExcelGeneratorPDFGenerator,而客户Client再通过服务提供者ReportService获取相应的报表打印功能

根据上面所述,得到如下类图:

代码实现:
    1. interfaceReportGenerator{
    1. publicvoidgenerate(Tabletable);
    1. }
    1. classExcelGeneratorimplementsReportGenerator{
    1. voidgenerate(Tabletable){
    1. System.out.println("generateanExcelreport...");
    1. }
    1. }
    1. classPDFGeneratorvoidgenerate(Tabletable){
    1. System.out.println("generateanPDFreport...");
    1. }
    1. }
    1. classReportService{
    1. //负责创建具体需要的报表生成
    1. privateReportGeneratorgenerator=newPDFGenerator();
    1. @H_301_263@//privatestaticReportGeneratorgenerator=newExcelGenerator();
    1. voidgetDailyReport(Datedate){
    1. table.setDate(date);
    1. @H_301_263@//...
    1. generator.generate(table);
    1. }
    1. voidgetMonthlyReport(Monthmonth){
    1. table.setMonth(month);
    1. @H_301_263@//...
    1. generator.generate(table);
    1. }
    1. }
    1. classClient{
    1. staticvoidmain(String[]args){
    1. ReportServicereportService=newReportService();
    1. reportService.getDailyReport(newDate());
    1. @H_301_263@//reportService.getMonthlyReport(newDate());
    1. }
    1. }
    问题描述:

    如上面代码中的注释所示,具体的报表生成器由ReportService类内部硬编码创建,由此ReportService已经直接依赖PDFGeneratorExcelGenerator,必须消除这一明显的紧耦合关系。

    解决方案: 引入容器

    引入一个中间管理者,也就是容器(Container),由其统一管理报表系统所涉及的对象(在这里是组件,我们将其称为Bean),包括ReportService和各个XXGenerator。在这里使用一个键-值对形式的HashMap实例来保存这些Bean

    得到类图如下:

    代码实现:
      1. classContainer{
      1. @H_301_263@//以键-值对形式保存各种所需组件Bean
      1. privatestaticMap<String,Object>beans;
      1. publicContainer(){
      1. beans=newHashMap<String,Object>();
      1. @H_301_263@//创建、保存具体的报表生起器
      1. ReportGeneratorreportGenerator=newPDFGenerator();
      1. beans.put("reportGenerator",reportGenerator);
      1. @H_301_263@//获取、管理ReportService的引用
      1. ReportServicereportService=newReportService();
      1. beans.put("reportService",reportService);
      1. }
      1. staticObjectgetBean(Stringid){
      1. returnbeans.get(id);
      1. }
      1. }
      1. @H_301_263@//消除紧耦合关系,由容器取而代之
      1. @H_301_263@//privatestaticReportGeneratorgenerator=newPDFGenerator();
      1. privateReportGeneratorgenerator=(ReportGenerator)Container.getBean("reportGenerator");
      1. voidgetDailyReport(Datedate){
      1. table.setDate(date);
      1. generator.generate(table);
      1. }
      1. voidgetMonthlyReport(Monthmonth){
      1. table.setMonth(month);
      1. generator.generate(table);
      1. }
      1. }
      1. voidmain(String[]args){
      1. Containercontainer=newContainer();
      1. ReportServicereportService=(ReportService)Container.getBean("reportService");
      1. reportService.getDailyReport(@H_301_263@//reportService.getMonthlyReport(newDate());
      1. }
      1. }
      时序图大致如下:

      效果

      如上面所示,ReportService不再与具体的ReportGenerator直接关联,已经用容器将接口和实现隔离开来了,提高了系统组件Bean的重用性,此时还可以使用配置文件Container中实时获取具体组件的定义。

      问题描述:

      然而,观察上面的类图,很容易发现ReportServiceContainer之间存在双向关联,彼此互相有依赖关系。并且,如果想要重用ReportService,由于它也是直接依赖于单独一个Container具体查找逻辑。若其他容器具体不同的组件查找机制(如JNDI),此时重用ReportService意味着需要修改Container的内部查找逻辑。

      解决方案: 引入 Service Locator

      再次引入一个间接层Service Locator,用于提供组件查找逻辑的接口,请看Wikipedia中的描述或者Java EE对其的描述1描述2。这样就能够将可能变化的点隔离开来。

      类图如下:

      代码实现:
        1. @H_301_263@//实际应用中可以是用interface来提供统一接口
        1. classServiceLocator{
        1. staticContainercontainer=newContainer();
        1. staticReportGeneratorgetReportGenerator(){
        1. return(ReportGenerator)container.getBean("reportGeneraator");
        1. }
        1. }
        1. privateReportGeneratorreportGenerator=ServiceLocator.getReportGenerator();
        1. @H_301_263@//...
        1. }


        小结:

        1、虽然讲了这么大篇幅还没有进入真正的主题——IoCDI,不过已经在一步步逼近了,下一篇应该会更精彩!在这里...

        2、可以很明显地看得出上面两中重新设计以解耦、隔离变化点都是通过引入间接层得以解决的。

        3、在看书过程中,我感觉《Spring攻略》一书中以“问题描述、解决方案、实现方法”方式的讲解比较容易理解和理清思路,故而也学习用这种方式来写。另,推荐该书以学习Spring框架(尽管目前我看得也不多)。

        猜你在找的设计模式相关文章