Spring Recipse精译(1)
第一章 IOC 和容器
1-1 使用容器管理组件
【问题】
面向对象设计的原理之一就是要将对象分组以重用。但如果没有一个统一的模块管理这些对象,而由它们自身管理创建和依赖,会引起对象间的紧耦合。
【方案】
使用容器来组织你的系统。容器将负责对象整个生命周期的管理和查找服务。
【详细】
接口和实现分离
例如你开发的系统有提供不同类型报表的功能,如 HTML 或 PDF 。针对面向对象设计的接口和实现分离原理,应该为生成报表定义一个公用的接口。假设报表由两维的表格数据生成。定义的接口如下:
Public interface ReportGenerator{
Public void generate(String[][] table);
}
接着建立两个类 HtmlReportGenerator 和 PdfReportGenerator 来实现 ReportGenerator 接口。
Public class HtmlReportGenerator implements ReportGenerator{
Public void generate(String[][] table){
System.out.println(“Generating HTML report”);
}
}
Public class PdfReportGenerator implements ReportGenerator{
Public void generate(String[][] table){
System.out.println(“Generating PDF report”);
}
}
建立服务类 ReportService ,由它提供多种报表服务。
Public class ReportService{
Private ReportGenerator reportGenerator = new PdfReportGenerator();
Public void generateAnnualReport(int year){
String[][] statistics = null;
//Gather statistics for the year
reportGenerator.generator(statistics);
}
Public void generateMonthlyReport(int year, int month){
String [][] statistics = null;
//Gather statistics for the monthe
reportGenerator.generate(statistics);
}
}
如下 UML 图说明了现在的 ReportService 类和 ReportGenerator 实现类的依赖关系
这会引起 ReportService 和 ReportGenerator 实现间的直接依赖关系。
引入容器
引入容器来管理对象。
Public class Container{
//The global instance of this Container class for the components to locate.
Public static Container instance;
//A map for storing the components with their IDs as the keys
Private Map<String, Object> components;
Public Container(){
Components = new HashMap<String, Object>();
Instance = this;
ReportGenerator reportGenerator = new PdfReportGenerator();
Components.put(“reportGenerator”, retportGenerator);
ReportSerice reportServie= new ReportSerice();
Component.put(“reportService”, reportService);
}
Public Object getComponent(String id){
Return components.get(id);
}
}
加入容器后 ReportService 如下
Public class ReportServie{
Private ReportGenerator reportGenerator = (ReportGenerator); Container.instance.getComponent(“reportGenerator”);
Public void generateAnnualReport(int year){ . . .
}
. . .
}
UML 图如下
可以看到 ReportSerice 这时依赖与容器,而容器依赖与 ReportGenerator 的实现。
测试类
Public class main{
Public static void main(String[] args){
Container container = new Container();
ReportService reportService = (ReportService) container.getComponent(“reportService”);
reportService.generateAnnualReport(2010);
}
}
1-2 使用服务定位器降低查找的复杂度
【问题】
在容器的管理下,组件依赖于它们的接口,而不是实现。需要硬编码。
【方案】
为降低查找组件的复杂度,引入服务定位器模式。用一个服务定位器来封装复杂的查找逻辑。
【详细】
Public class ServiceLocator{
Private static Container container = Container.instance;
Public static ReportGenerator getReportGenerator(){
Return (ReportGenerator) container.getComponent(“reportGenerator”);
}
}
在服务类中可以直接使用服务定位器查找依赖对象。
Public class ReportSerice{
Private ReportGenerator reportGenerator =
ServiceLocator.getReportGenerator();
Public void generatorAnnualReport(int year){
…
}
}
UML 类图如下:
1-3 应用 IoC 和 DI
【问题】
当组件需要一个外表的资源,如数据源。最直接的方式是进行查找。我们称这种方式为主动查找。如服务定位器。
【方案】
更好的获取资源的方式是使用 IoC 。思想是反转资源获取的方向。在典型的查找中,组件向容器发送请求查找,容器返回查找资源。在 IoC 中,容器主动向被管组件分发资源。一个组件只能被动的接受资源。这被称之为被动查找。
IoC 是理论上的概念,而 DI 依赖注入是具体的设计模式。在 DI 模式中,容器负责为每个组件,在预先决定的方式,注入适当的资源。
【详细】
依据 DI 模式,在 ReportService 中为 ReportGenerator 属性加入 setter 方法。
Public class ReportService{
Private ReportGenerator reportGenerator; // No need to look up actively
Public void setReportGenerator(ReportGenerator reportGenerator){
This.reportGenerator = reportGenerator;
}
Public void generatorAnnualReport(int year){
…
}
}
Public class Container{
//No need to expose itself for the components to locate
//public static Container instance
Private Map<String , Object> components;
Public Container(){
Components = new HashMap<String, Object>();
//No need to expose the current instance of container
//instance = this;
ReportGenerator reportGenerator = new PdfReportGenerator();
Components.put(“reportGenerator”, reportGenerator);
ReportService reportService = new ReportService();
reportService.setReportGenerator(reportGenerator);
components.put(“reportService”, reportService);
}
Public Object getComponent(String id){
Return components.get(id);
}
}
IoC 原理就是俗称的好莱坞原理。
不要给我打电话,我会给你打电话的。
1-4 理解不同类型的 DI
【问题】
需要不同的 DI 类型
【方案】
三种类型:
接口注入
Setter 注入
构造器注入
【详细】
Setter 注入
Setter 注入是最常用的注入类型。通过组件中的 setter 方法注入它的依赖。
构造器注入
通过组件的构造器注入它的依赖。
接口注入
基本上不用这种方式。
1-5 用配置文件配置容器
【问题】
使用容器来管理组件以及它们的依赖关系时,配置信息如果使用硬编码方式,不易于维护。
【方案】
使用基于文本的、语义强的 XML 或 properties 文件。它们不需要重新编译。