原文地址:http://haolloyin.blog.51cto.com/1177454/458416/
近来总是接触到?IoC(Inversion of Control,控制反转)、DI(Dependency Injection,依赖注入)等编程原则或者模式,而这些是著名?Java?框架?Spring、Struts?等的核心所在。针对此查了Wikipedia?中各个条目,并从图书馆借来相关书籍,阅读后有些理解,现结合书中的讲解以及自己的加工整理如下:
?
问题描述:
开发一个能够按照不同要求生成Excel或?PDF?格式的报表的系统,例如日报表、月报表等等。
解决方案:
根据“面向接口编程”的原则,应该分离接口与实现,即将生成报表的功能提取为一个通用接口ReportGenerator,并提供生成?Excel?和?PDF格式报表的两个实现类?ExcelGenerator?和PDFGenerator,而客户Client?再通过服务提供者?ReportService?获取相应的报表打印功能。
实现方法:
根据上面所述,得到如下类图:

代码实现:
?
- interface?ReportGenerator?{?
- ????public?void?generate(Table?table);?
- }?
- ?
- class?ExcelGenerator?implements?ReportGenerator?{?
- ????void?generate(Table?table)?{?
- ????????System.out.println("generate?an?Excel?report?...");?
- ????}?
- }?
- ?
- class?PDFGenerator?void?generate(Table?table)?{?
- ????????System.out.println("generate?an?PDF?report?...");?
- ????}?
- }?
- ?
- class?ReportService?{?
- ?????
- ????private?ReportGenerator?generator?=?new?PDFGenerator();?
- ?????
- ?????
- ????void?getDailyReport(Date?date)?{?
- ????????table.setDate(date);?
- ?????????
- ????????generator.generate(table);?
- ????}?
- ?????
- ????void?getMonthlyReport(Month?month)?{?
- ????????table.setMonth(month);?
- ?????????
- ????????generator.generate(table);?
- ????}?
- }?
- ?
- ?
- class?Client?{?
- ????static?void?main(String[]?args)?{?
- ????????ReportService?reportService?=?new?ReportService();?
- ????????reportService.getDailyReport(new?Date());?
- ?????????
- ????}?
- }?
?
问题描述:
如上面代码中的注释所示,具体的报表生成器由?ReportService?类内部硬编码创建,由此ReportService?已经直接依赖于?PDFGenerator?或?ExcelGenerator?,必须消除这一明显的紧耦合关系。
解决方案:
引入容器
引入一个中间管理者,也就是容器(Container),由其统一管理报表系统所涉及的对象(在这里是组件,我们将其称为?Bean),包括?ReportService?和各个?XXGenerator?。在这里使用一个键-值对形式的?HashMap?实例来保存这些?Bean。
实现方法:
得到类图如下:

代码实现:
class?Container?{?
?????
????private?static?Map<String,?Object>?beans;?
?????
????public?Container()?{?
????????beans?=?new?HashMap<String,?Object>();?
?????????
?????????
????????ReportGenerator?reportGenerator?=?new?PDFGenerator();?
????????beans.put("reportGenerator",?reportGenerator);?
?????????
?????????
????????ReportService?reportService?=?new?ReportService();?
????????beans.put("reportService",?reportService);?
????}?
?????
????static?Object?getBean(String?id)?{?
????????return?beans.get(id);?
????}?
}?
?
class?ReportService?{?
?????
?????
????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?不再与具体的?ReportGenerator?直接关联,已经用容器将接口和实现隔离开来了,提高了系统组件?Bean?的重用性,此时还可以使用配置文件在?Container?中实时获取具体组件的定义。
然而,观察上面的类图,很容易发现?ReportService?与?Container?之间存在双向关联,彼此互相有依赖关系。并且,如果想要重用?ReportService,由于它也是直接依赖于单独一个?Container?的具体查找逻辑。若其他容器具体不同的组件查找机制(如?JNDI),此时重用?ReportService?意味着需要修改?Container?的内部查找逻辑。
解决方案:
引入
?Service Locator
再次引入一个间接层?Service Locator,用于提供组件查找逻辑的接口,请看Wikipedia?中的描述?或者?Java EE?对其的描述1?、描述2?。这样就能够将可能变化的点隔离开来。
实现方法:
类图如下:

代码实现:
?
class?ServiceLocator?{?
????static?Container?container?=?new?Container();?
?????
????static?ReportGenerator?getReportGenerator()?{?
????????return?(ReportGenerator)container.getBean("reportGeneraator");?
????}?
}?
?
class?ReportService?{?
????private?ReportGenerator?reportGenerator?=?ServiceLocator.getReportGenerator();?
?????
?????
}?
?
小结:
1、虽然讲了这么大篇幅还没有进入真正的主题——IoC、DI,不过已经在一步步逼近了,下一篇应该会更精彩!在这里...
2、可以很明显地看得出上面两中重新设计以解耦、隔离变化点都是通过引入间接层得以解决的。
3、在看书过程中,我感觉《Spring?攻略》一书中以“问题描述、解决方案、实现方法”方式的讲解比较容易理解和理清思路,故而也学习用这种方式来写。另,推荐该书以学习?Spring?框架(尽管目前我看得也不多)。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!