加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-14 05:14:57 所属栏目:百科 来源:网络整理
导读:原文地址:http://haolloyin.blog.51cto.com/1177454/458416/ 近来总是接触到 ?IoC ( Inversion of Control ,控制反转)、 DI ( Dependency Injection ,依赖注入)等编程原则或者模式,而这些是著名 ?Java? 框架 ?Spring 、 Struts? 等的核心所在。针对

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


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

?
问题描述:

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

解决方案:

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

实现方法:
根据上面所述,得到如下类图:

代码实现:
?
 
 
  1. interface?ReportGenerator?{?
  2. ????public?void?generate(Table?table);?
  3. }?
  4. ?
  5. class?ExcelGenerator?implements?ReportGenerator?{?
  6. ????void?generate(Table?table)?{?
  7. ????????System.out.println("generate?an?Excel?report?...");?
  8. ????}?
  9. }?
  10. ?
  11. class?PDFGenerator?void?generate(Table?table)?{?
  12. ????????System.out.println("generate?an?PDF?report?...");?
  13. ????}?
  14. }?
  15. ?
  16. class?ReportService?{?
  17. ????//?负责创建具体需要的报表生成器?
  18. ????private?ReportGenerator?generator?=?new?PDFGenerator();?
  19. ????//?private?static?ReportGenerator?generator?=?new?ExcelGenerator();?
  20. ?????
  21. ????void?getDailyReport(Date?date)?{?
  22. ????????table.setDate(date);?
  23. ????????//?...?
  24. ????????generator.generate(table);?
  25. ????}?
  26. ?????
  27. ????void?getMonthlyReport(Month?month)?{?
  28. ????????table.setMonth(month);?
  29. ????????//?...?
  30. ????????generator.generate(table);?
  31. ????}?
  32. }?
  33. ?
  34. ?
  35. class?Client?{?
  36. ????static?void?main(String[]?args)?{?
  37. ????????ReportService?reportService?=?new?ReportService();?
  38. ????????reportService.getDailyReport(new?Date());?
  39. ????????//reportService.getMonthlyReport(new?Date());?
  40. ????}?
  41. }?
?
问题描述:

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

解决方案: 引入容器

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

实现方法:
得到类图如下:

代码实现:
class?Container?{?
  
  
  • ????//?以键-值对形式保存各种所需组件?Bean?
  • ????private?static?Map<String,?Object>?beans;?
  • ?????
  • ????public?Container()?{?
  • ????????beans?=?new?HashMap<String,?Object>();?
  • ?????????
  • ????????//?创建、保存具体的报表生起器?
  • ????????ReportGenerator?reportGenerator?=?new?PDFGenerator();?
  • ????????beans.put("reportGenerator",?reportGenerator);?
  • ?????????
  • ????????//?获取、管理?ReportService?的引用?
  • ????????ReportService?reportService?=?new?ReportService();?
  • ????????beans.put("reportService",?reportService);?
  • ????}?
  • ?????
  • ????static?Object?getBean(String?id)?{?
  • ????????return?beans.get(id);?
  • ????}?
  • }?
  • ?
  • class?ReportService?{?
  • ????//?消除紧耦合关系,由容器取而代之?
  • ????//?private?static?ReportGenerator?generator?=?new?PDFGenerator();?
  • ????private?ReportGenerator?generator?=?(ReportGenerator)?Container.getBean("reportGenerator");?
  • ?
  • ????void?getDailyReport(Date?date)?{?
  • ????????table.setDate(date);?
  • ????????generator.generate(table);?
  • ????}?
  • ?????
  • ????void?getMonthlyReport(Month?month)?{?
  • ????????table.setMonth(month);?
  • ????????generator.generate(table);?
  • ????}?
  • }?
  • ?
  • void?main(String[]?args)?{?
  • ????????Container?container?=?new?Container();?
  • ????????ReportService?reportService?=?(ReportService)Container.getBean("reportService");?
  • ????????reportService.getDailyReport(new?Date());?
  • ????????//reportService.getMonthlyReport(new?Date());?
  • ????}?
  • }?
  • 时序图大致如下:

    效果:

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

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

    解决方案: 引入 ?Service Locator

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

    实现方法:
    类图如下:

    代码实现:
  • //?实际应用中可以是用?interface?来提供统一接口?
  • class?ServiceLocator?{?
  • ????static?Container?container?=?new?Container();?
  • ?????
  • ????static?ReportGenerator?getReportGenerator()?{?
  • ????????return?(ReportGenerator)container.getBean("reportGeneraator");?
  • ????}?
  • }?
  • ?
  • class?ReportService?{?
  • ????private?ReportGenerator?reportGenerator?=?ServiceLocator.getReportGenerator();?
  • ?????
  • ????//?...?
  • }?

  • ?

    小结:

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

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

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

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读