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

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

发布时间:2020-12-14 05:44:21 所属栏目:百科 来源:网络整理
导读:? 近来总是接触到 IoC ( Inversion of Control ,控制反转)、 DI ( Dependency Injection ,依赖注入)等编程原则或者模式,而这些是著名 Java 框架 Spring 、 Struts 等的核心所在。针对此查了 Wikipedia 中各个条目,并从图书馆借来相关书籍,阅读后有

?近来总是接触到 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. ????public?void?generate(Table?table)?{?
  7. ????????System.out.println("generate?an?Excel?report?...");?
  8. ????}?
  9. }?
  10. ?
  11. class?PDFGenerator?implements?ReportGenerator?{?
  12. ????public?void?generate(Table?table)?{?
  13. ????????System.out.println("generate?an?PDF?report?...");?
  14. ????}?
  15. }?
  16. ?
  17. class?ReportService?{?
  18. ????//?负责创建具体需要的报表生成器?
  19. ????private?ReportGenerator?generator?=?new?PDFGenerator();?
  20. ????//?private?static?ReportGenerator?generator?=?new?ExcelGenerator();?
  21. ?????
  22. ????public?void?getDailyReport(Date?date)?{?
  23. ????????table.setDate(date);?
  24. ????????//?...?
  25. ????????generator.generate(table);?
  26. ????}?
  27. ?????
  28. ????public?void?getMonthlyReport(Month?month)?{?
  29. ????????table.setMonth(month);?
  30. ????????//?...?
  31. ????????generator.generate(table);?
  32. ????}?
  33. }?
  34. ?
  35. ?
  36. public?class?Client?{?
  37. ????public?static?void?main(String[]?args)?{?
  38. ????????ReportService?reportService?=?new?ReportService();?
  39. ????????reportService.getDailyReport(new?Date());?
  40. ????????//reportService.getMonthlyReport(new?Date());?
  41. ????}?
  42. }?
?
?
问题描述:

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

?
解决方案: 引入容器

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

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

?
代码实现:
 
 
  1. class?Container?{?
  2. ????//?以键-值对形式保存各种所需组件?Bean?
  3. ????private?static?Map<String,?Object>?beans;?
  4. ?????
  5. ????public?Container()?{?
  6. ????????beans?=?new?HashMap<String,?Object>();?
  7. ?????????
  8. ????????//?创建、保存具体的报表生起器?
  9. ????????ReportGenerator?reportGenerator?=?new?PDFGenerator();?
  10. ????????beans.put("reportGenerator",?reportGenerator);?
  11. ?????????
  12. ????????//?获取、管理?ReportService?的引用?
  13. ????????ReportService?reportService?=?new?ReportService();?
  14. ????????beans.put("reportService",?reportService);?
  15. ????}?
  16. ?????
  17. ????public?static?Object?getBean(String?id)?{?
  18. ????????return?beans.get(id);?
  19. ????}?
  20. }?
  21. ?
  22. class?ReportService?{?
  23. ????//?消除紧耦合关系,由容器取而代之?
  24. ????//?private?static?ReportGenerator?generator?=?new?PDFGenerator();?
  25. ????private?ReportGenerator?generator?=?(ReportGenerator)?Container.getBean("reportGenerator");?
  26. ?
  27. ????public?void?getDailyReport(Date?date)?{?
  28. ????????table.setDate(date);?
  29. ????????generator.generate(table);?
  30. ????}?
  31. ?????
  32. ????public?void?getMonthlyReport(Month?month)?{?
  33. ????????table.setMonth(month);?
  34. ????????generator.generate(table);?
  35. ????}?
  36. }?
  37. ?
  38. public?class?Client?{?
  39. ????public?static?void?main(String[]?args)?{?
  40. ????????Container?container?=?new?Container();?
  41. ????????ReportService?reportService?=?(ReportService)Container.getBean("reportService");?
  42. ????????reportService.getDailyReport(new?Date());?
  43. ????????//reportService.getMonthlyReport(new?Date());?
  44. ????}?
  45. }?
?
时序图大致如下:

?
效果:

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

?
?
问题描述:

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

?
解决方案: 引入 Service Locator

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

?
实现方法:
类图如下:

?
代码实现:
?
 
 
  1. //?实际应用中可以是用?interface?来提供统一接口?
  2. class?ServiceLocator?{?
  3. ????private?static?Container?container?=?new?Container();?
  4. ?????
  5. ????public?static?ReportGenerator?getReportGenerator()?{?
  6. ????????return?(ReportGenerator)container.getBean("reportGeneraator");?
  7. ????}?
  8. }?
  9. ?
  10. class?ReportService?{?
  11. ????private?ReportGenerator?reportGenerator?=?ServiceLocator.getReportGenerator();?
  12. ?????
  13. ????//?...?
  14. }?


?

小结:

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

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

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

?
以下文章你可能也会感兴趣:

Factory Method)工厂方法模式的Java实现

Java RMI 框架的工厂方法模式实现

Mediator)中介者模式的Java实现(加修改)

Dynamic Proxy)动态代理模式的Java实现

Template Method)模板方法模式的Java实现

(编辑:李大同)

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

    推荐文章
      热点阅读