Spring实现的两种设计模式
Spring 实现两种设计模式
在Spring 中大量使用的以下两种设计模式:
工厂模式 单态模式
工厂模式可将Java 对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则
(接口) ,而不必关心实例的具体实现过程。这是面向接口编程的优势,能提高程序的解耦,避免所有的类以硬
编码方式耦合在一起。
如果所有的类直接藕合,极易形成"骨牌效应",假如B 类调用了A 类,一旦A 类需要修改,则B 类也需要修改:
假如C 类调用了B 类,则C 类也需要修改......依次类推,从而导致整个系统都需要改写。造成"牵一发而动全
身",而系统重构的代价是相当高的。
Spring 倡导"面向接口编程",可以避免上述的问题,使设计良好的架构可保证系统重构的工作被封闭在重构的
层内,绝不会影响其他层,这可以在本书后面的示例中看到。
Spring 容器是实例化和管理全部bean 的工厂, Spring 默认将所有的bean 设置成单态模式,无须自己完成单
态模式,即对所有相同id 的bean 请求都将返回同一个共享实例。因此,单态模式可大大降低Java 对象在创建
和销毁时的系统开销。
1 单态模式的回顾
单态模式限制了类实例的创建,但采用这种模式设计的类,可以保证仅有一个实例,并可提供访问该实例的全
局访问点。J2 EE 应用的大量组件,都需要保证一个类只有一个实例。比如数据库引擎访问点只能有一个。
更多的时候,为了提高性能,程序应尽量减少Java 对象的创建和销毁时的开销。使用单态模式可避免Java 类
被多次实例化,让相同类的全部实例共享同一内存区。
为了防止单态模式的类被多次实例化,应将类的构造器设成私有,这样就保证了只能通过静态方法获得类实例
。而该静态方法则保证每次返回的实例都是同一个,这就需将该类的实例设置成类属性,由于该属性需要被静
态方法访问,因此该属性应设成静态属性。
下面给出单态模式的示例代码: TestSingleton.java
//单态模式测试类
public class TestSingleton {
//该类的一个普通属性。
int value ;
//使用静态属性类保存该类的一个实例。
private static TestSingleton instance;
//构造器私有化,避免该类被多次实例。
private TestSingleton(){
System.out.println("正在执行构造器...");
}
// 提供静态方法来返回该类的实例。
public static TestSingleton getInstance(){
// 实例化类实例前,先检查该类的实例是否存
if (instance == null){
//如果不存在,则新建一个实例。
instance = new TestSingleton();
}
// 返回该类的成员变量:该类的实例。
return instance;
}
//以下提供对普通属性value 的setter 和getter 方法
public int getValue(){
return value;
}
public void setValue() {
this.value =value;
}
public static void main(String[] args) {
TestSingleton t1 = TestSingleton .getInstance();
TestSingleton t2 = TestSingleton .getInstance();
t2.setValue(9);
System.out.println( t1 ==t2 );
}
}
从程序最后的打印结果可以看出,该类的两个实例完全相同。这证明单态模式类的全部实例是同一共享实例。
程序里虽然获得了类的两个实例,但实际上只执行一次器,因为对于单态模式的类,无论有多少次的创建实例
请求,都只执行一次构造器。
2 工厂模式的回顾
工厂模式是根据调用数据返回某个类的一个实例,此类可以是多个类的某一个类。通常,这些类满足共同的规
则(接口)或父类。调用者只关心工厂生产的实例是否满足
某种规范,即实现的某个接口是否可供自己正常调用(调用者仅仅使用)。该模式给对象之间作出了清晰的角色
划分,降低程序的棋合。
接口产生的全部实例通常用于实现相同接口,接口里定义了全部实例共同拥有的方法,这些方法在不同的实现
类中实现的方式不同。从而使程序调用者无须关心方法的具体实现,降低了系统异构的代价。
下面是工厂模式的示例代码:
//Person 接口定义
public interface Person{
public String sayHello(String name);
public String sayGoodBye(String name);
}
该接口定义了Person 的规范,规范要求实现该接口的类必须具有这两个方法:能打招呼,能告别。
//American 类实现Person 接口
public class American implements Person{
public String sayHello(String name){
return name + ",Hello";
}
public String sayGoodBye(String name){
return name + ",Good Bye";
}
}
下面是实现Person 接口的另一个实现类: Chinese.java
public class Chinese implements Person{
public String sayHello(String name){
return name + ",您好";
}
public String sayGoodBye(String name){
return name + ",下次再见";
}
}
然后看Person 工厂的代码:
public class PersonFactory{
public Person getPerson(String ethnic){
if (ethnic.equalsIgnoreCase("chin")){
return new Chinese();
}
else{
return new American();
}
}
}
以上是最简单的工厂模式框架,其主程序部分如下:
public class FactroyTest
{
public static void main(String[] args)
{
//创建PersonFactory的实例,获得工厂实例
PersonFactory pf = new PersonFactory();
//定义接口Person 的实例,面向接口编程
Person p = null;
//使用工厂获得Person 的实例
p = pf.getPerson("chin");
//下面调用Person 接口的方法
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodBye("wawa"));
//使用工厂获得Person 的另一个实例
p = pf.getPerson("ame");
//再次调用Person 接口的方法
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodBye("wawa"));
}
}
由此可看出,主程序从Person 接口的具体类中解糯出来,而且程序调用者无须关心
Person 的实例化过程,主程序仅仅与工厂服务定位结合在一起,可获得所有工厂能产生
的实例。具体类的变化,接口无须发生任何改变,调用者程序代码部分也无须发生任何
改动。
下面是Spring 对这两种模式的实现。
3 Spring 对单态与工厂模式的实现
随着Spring 提供工厂模式的实现,在使用Spring 时,无须自己提供工厂类。因为Spring
容器是最大的工厂,而且是个功能超强的工厂。Spring 使用配置文件管理所有的bean ,其
配置文件中bean 由Spring 工厂负责生成和管理。下面是关于两个实例的配置文件:
<beans>
<bean id="chinese" class="Chinese"/>
<bean id="american" class="American"/>
</beans>
主程序部分如下:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class SpringTest{
public static void main(String[] args){
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
Person p = null;
p = (Person)ctx.getBean("chinese");
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodBye("wawa"));
p = (Person)ctx.getBean("american");
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodBye("wawa"));
}
}
使用Spring 时:即使没有工厂类PersonFactory ,程序一样可以使用工厂模式, Spring
完全可以提供所有工厂模式的功能。
下面对主程序部分进行简单的修改:
public class SpringTest{
public static void main(String[] args){
//实例化Spring 容器
ApplicationContext ctx =new FileSystemXmlApplicationContext("bean.xml");
//定义Person 接口的实例pl
Person pl = null;
//通过Spring 上下文获得chinese 实例
pl = (Person)ctx.getBean("chinese");
//定义Person 接口的实例pl
Person p2 = null;
p2 = (Person)ctx.getBean("chinese");
System.out.println( p1 == p2);
}
}
程序执行的结果是: true
表明Spring 对接受容器管理的全部bean,默认采用单态模式管理。笔者建议不要随便更改bean 的行为方式。
因为在性能上,单态的bean 比非单态的bean 更优秀。
仔细检查上面的代码,就会发现如下特点:
1除测试用的主程序部分外,代码并未出现Spring 特定的类和接口。
2调用者代码,也就是测试用的主程序部分,仅仅面向Person 接口编程,而无须知道实现类的具体名称。同时
,可以通过修改配置文件来切换底层的具体实现类。
3由于工厂无须多个实例,因此工厂应该采用单态模式设计。其中Spring 的上下文,也就是Spring 工厂,已被
设计成单态的。
Spring 工厂模式,不仅提供了创建bean 的功能,还提供对bean 生命周期的管理。最重要的是还可管理bean
与bean 之间的依赖关系。