Spring 实现两种设计模式:工厂模式和单态模式(单例模式)

时间:2021-10-18 19:51:15

本文摘自:李刚 著 《轻量级 Java EE企业应用实战 Struts2+Spring+hibernate整合开发》

在Spring 中大量使用的以下两种设计模式:工厂模式单态模式

工厂模式可将Java 对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则(接口) ,而不必关心实例的具体实现过程。这是面向接口编程的优势,能提高程序的解耦,避免所有的类以硬编码方式耦合在一起。

如果所有的类直接耦合,极易形成"骨牌效应",假如B 类调用了A 类,一旦A 类需要修改,则B 类也需要修改;假如C 类调用了B 类,则C 类也需要修改......依次类推,从而导致整个系统都需要改写。造成"牵一发而动全身",而系统重构的代价是相当高的。

Spring 倡导”面向接口编程“,可以避免上述的问题,使设计良好的架构可保证系统重构的工作被封闭在重构的层内,绝不会影响其他层。

Spring 容器是实例化和管理全部bean 的工厂,Spring 默认将所有的bean 设置成单态模式,无须自己完成单态模式,即对所有相同id 的bean 请求都将返回同一个共享实例。因此,单态模式可大大降低Java 对象在创建和销毁时的系统开销。

一. 单态模式的回顾

单态模式限制了类实例的创建,但采用这种模式设计的类,可以保证仅有一个实例,并可提供访问该实例的全局访问点。J2EE应用的大量组件,都需要保证一个类只有一个实例。比如数据库引擎访问点只能有一个。

更多的时候,为了提高性能,程序应尽量减少Java 对象的创建和销毁时的开销。使用单态模式可避免Java 类被多次实例化,让相同类的全部实例共享同一内存区。

为了防止单态模式的类被多次实例化,应将类的构造器设成私有,这样就保证了只能通过静态方法获得类实例。而该静态方法则保证每次返回的实例都是同一个,这就需将该类的实例设置成类属性,由于该属性需要被静态方法访问,因此该属性应设成静态属性。

下面给出单态模式的示例代码:

  1. package ppp;
  2. //单态模式测试类
  3. public class SingletonTest {
  4. //该类的一个普通属性
  5. int value;
  6. //使用静态属性保存该类的一个实例
  7. private static SingletonTest instance;
  8. //构造器私有化,避免该类被多次实例化
  9. private SingletonTest(){
  10. System.out.println("正在执行构造器...");
  11. }
  12. //提供静态方法返回该类实例
  13. public static SingletonTest getInstance(){
  14. //实例化类实例前,先检查该实例是否存在
  15. if(instance == null){
  16. //如果不存在,则新建一个实例
  17. instance = new SingletonTest();
  18. }
  19. //返回该类的成员变量:该类的实例
  20. return instance;
  21. }
  22. //以下提供对普通属性value的getter和setter方法
  23. public int getValue(){
  24. return value;
  25. }
  26. public void setValue(int values){
  27. this.value = values;
  28. }
  29. public static void main(String args[]){
  30. SingletonTest t1 = SingletonTest.getInstance();
  31. SingletonTest t2 = SingletonTest.getInstance();
  32. t2.setValue(9);
  33. System.out.println(t1 == t2);
  34. }
  35. }

从程序最后的打印结果可以看出,该类的两个实例完全相同。这证明单态模式类的全部实例是同一共享实例。程序里虽然获得了类的两个实例,但实际上只执行一次构造器,因为对于单态模式的类,无论有多少次的创建实例请求,都只执行一次构造器。

二. 工厂模式的回顾

工厂模式是根据调用数据返回某个类的一个实例,此类可以是多个类的某一个类。通常,这些类满足共同的规则(接口)或父类。调用者只关心工厂生产的实例是否满足某种规范,即实现的某个接口是否可供自己正常调用(调用者仅仅使用)。该模式给对象之间作出了清晰的角色划分,降低程序的耦合。

接口产生的全部实例通常用于实现相同接口,接口里定义了全部实例共同拥有的方法,这些方法在不同的实现类中实现的方式不同。从而使程序调用者无须关心方法的具体实现,降低了系统异构的代价。

下面是工厂模式的示例代码:

  1. package ppp;
  2. //Person接口定义
  3. public interface Person {
  4. public String sayHello(String name);
  5. public String sayGoodbye(String name);
  6. }

该接口定义了Person规范,规范要求实现该接口的类必须具有以下两个的方法:能打招呼,能告别。

  1. package ppp;
  2. //American类实现Person接口
  3. public class American implements Person {
  4. public String sayHello(String name){
  5. return name+",hello";
  6. }
  7. public String sayGoodbye(String name)
  8. {
  9. return name+",goodbye";
  10. }
  11. }

下面是Person类的另一个实现类:Chinese

  1. package ppp;
  2. //Chinese类实现Person接口
  3. public class Chinese implements Person {
  4. public String sayHello(String name){
  5. return name+",您好";
  6. }
  7. public String sayGoodbye(String name)
  8. {
  9. return name+",下次再见";
  10. }
  11. }

然后再看Person工厂的代码:

  1. package ppp;
  2. public class PersonFactory {
  3. public Person getPerson(String ethnic)
  4. {
  5. if(ethnic.equalsIgnoreCase("chin"))
  6. {
  7. return new Chinese();
  8. }else{
  9. return new American();
  10. }
  11. }
  12. }

以上是最简单的工厂模式框架,其主程序如下:

  1. package ppp;
  2. public class FactoryTest {
  3. public static void main(String[] args){
  4. //创建PersonFactory实例 ,获得工厂实例
  5. PersonFactory pf = new PersonFactory();
  6. //定义接口Person实例,面向接口编程
  7. Person p = null;
  8. //使用工厂获得person实例
  9. p = pf.getPerson("chin");
  10. //下面调用Person接口方法
  11. System.out.println(p.sayHello("wawa"));
  12. System.out.println(p.sayGoodbye("wawa"));
  13. //使用工厂获得Person的另一个实例
  14. p = pf.getPerson("ame");
  15. //再次调用Person接口的方法
  16. System.out.println(p.sayHello("wawa"));
  17. System.out.println(p.sayGoodbye("wawa"));
  18. }
  19. }

由此可看出,主程序从Person 接口的具体类中解耦出来,而且程序调用者无须关心Person 的实例化过程,主程序仅仅与工厂服务定位结合在一起,可获得所有工厂能产生的实例。具体类的变化,接口无须发生任何改变,调用者程序代码部分也无须发生任何改动。
下面是Spring 对这两种模式的实现。

三. Spring 对单态与工厂模式的实现

随着Spring 提供工厂模式的实现,在使用Spring 时,无须自己提供工厂类。因为Spring容器是最大的工厂,而且是个功能超强的工厂。Spring 使用配置文件管理所有的bean ,其配置文件中bean 由Spring 工厂负责生成和管理。

下面是关于两个实例的配置文件:

  1. <!--下面是xml文件的文件头-->
  2. <?xml version = "1.0" encoding = "gb2312"?>
  3. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
  4. "http://www.springsource.org/dtd/spring-beans.dtd">
  5. <!--beans是Spring配置文件的根元素-->
  6. <beans>
  7. <!--定义第一个bean,该bean的id是chinese-->
  8. <bean id = "chinese" class = "ppp.Chinese"/>
  9. <!--定义第二个bean,该bean的id是American-->
  10. <bean id = "american" class = "ppp.American"/>
  11. </beans>

主程序部分如下:

  1. package ppp;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.FileSystemXmlApplicationContext;
  4. public class SpringTest {
  5. public static void main(String[] args) {
  6. //实例化Spring容器
  7. ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
  8. //定义Person接口实例
  9. Person p = null;
  10. //通过Spring上下文获得Chinese实例
  11. p = (Person)ctx.getBean("chinese");
  12. //执行chinese实例的方法
  13. System.out.println(p.sayHello("wawa"));
  14. System.out.println(p.sayGoodbye("wawa"));
  15. p = (Person)ctx.getBean("american");
  16. System.out.println(p.sayHello("wawa"));
  17. System.out.println(p.sayGoodbye("wawa"));
  18. }
  19. }

使用Spring 时,即使没有工厂类PersonFactory ,程序一样可以使用工厂模式, Spring完全可以提供所有工厂模式的功能。

下面对主程序部分进行简单的修改:

  1. package ppp;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.FileSystemXmlApplicationContext;
  4. public class SpringTest{
  5. public static void main(String[] args){
  6. //实例化Spring容器
  7. ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
  8. //定义p1接口的实例p1
  9. Person p1 = null;
  10. //通过Spring上下文获得Chinese实例
  11. p1 = (Person)ctx.getBean("Chinese");
  12. //定义p2接口的实例p2
  13. Person p2 = null;
  14. p2 = (Person)ctx.getBean("Chinese");
  15. System.out.println(p1 == p2);
  16. }
  17. }

程序的执行结果是:true

表明Spring对接受容器管理的全部的bean,默认采用单态模式管理,建议不要随意更改bean的行为方式。因为从性能上讲,单态的bean比非单态的bean性能更为优秀。

仔细检查上面的代码就会发现如下的特点;

(1)除测试部分的主程序外,代码并未出现Spring的特定类和接口。

(2)调用者的代码,也就是测试用的主程序部分,仅仅面向Person的接口编程,而无需知道实现类的具体名称。同时,通过修改配置文件来彻底切换底层的具体实现类。

(3)由于厂无需多个实例,因此工厂应该采用单态模式设计,其中Spring上下文也就是Spring工厂,已被设计成单态。

Spring工厂模式,不仅提供了创建bean的功能,还提供了对bean的生命周期的管理。最重要的是还以管理bean和bean之间的依赖关系。

Spring 实现两种设计模式:工厂模式和单态模式(单例模式)的更多相关文章

  1. Spring主要用到两种设计模式

    Spring主要用到两种设计模式 1.工厂模式 Spring容器就是实例化和管理全部Bean的工厂. 工厂模式可以将Java对象的调用者从被调用者的实现逻辑中分离出来. 调用者只关心被调用者必须满足的 ...

  2. 两种设计模式和XML解析

    两种设计模式 1.单例模式 模式的保证步骤:单例(是说在一个类中只能有一个对象)三条件 1.1类构造设置私有  private  Play() { } 1.2 定义一个私有的静态的  类类型  变量 ...

  3. 浅谈Spring的两种配置容器

    浅谈Spring的两种配置容器 原文:https://www.jb51.net/article/126295.htm 更新时间:2017年10月20日 08:44:41   作者:黄小鱼ZZZ     ...

  4. struts2和spring的两种整合方式

    首先,来看看如何让Spring 来管理Action. 在struts.xml中加入 <constant name="struts.objectFactory" value=& ...

  5. k8s的两种网络方案与多种工作模式&lbrack;flannel与calico&rsqb;

    k8s的两种网络方案与多种工作模式 1. Flannel: flannel有三种工作模式: 1. vxlan(隧道方案) 2. host-gw(路由方案) 2. udp(在用户态实现的数据封装解封装, ...

  6. Java设计模式(2)单态模式(Singleton模式)

    定义:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作. 还有,singleton能够被状态化 ...

  7. PHP中的抽象类与抽象方法&sol;静态属性和静态方法&sol;PHP中的单利模式(单态模式)&sol;串行化与反串行化&lpar;序列化与反序列化&rpar;&sol;约束类型&sol;魔术方法小结

      前  言  OOP  学习了好久的PHP,今天来总结一下PHP中的抽象类与抽象方法/静态属性和静态方法/PHP中的单利模式(单态模式)/串行化与反串行化(序列化与反序列化). 1  PHP中的抽象 ...

  8. java&colon;常用的两种设计模式(单例模式和工厂模式)

    一.单例模式:即一个类由始至终只有一个实例.有两种实现方式(1)定义一个类,它的构造方法是私有的,有一个私有的静态的该类的变量在初始化的时候就实例化,通过一个公有的静态的方法获取该对象.Java代码  ...

  9. 23种设计模式--工厂模式-Factory Pattern

    一.工厂模式的介绍       工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...

随机推荐

  1. iOS开发&comma;音效的播放简单实现以及音效播放的简单封装

    一.音效的播放简单实现 二.音效播放的封装 -- 封装思路:将生成的SystemSoundID存放到字典中,每次播放的时候从字典中取出对应的SystemSoundID,没有的话再创建 头文件中定义类方 ...

  2. django允许跨域备忘笔记

    详细信息请拜读网址:https://github.com/ottoyiu/django-cors-headers/ 安装: 在virtaulenv环境中执行 pip install django-co ...

  3. &lbrack;转载&rsqb;理解HTML语义化

    声明: 本文转载于:freeyiyi1993博客. 原文地址:http://www.cnblogs.com/freeyiyi1993/p/3615179.html 1.什么是HTML语义化? < ...

  4. 转 15款免费WiFi(入侵破解)安全测试工具

    转:http://www.ctocio.com/security/cloudsecurity/6594.html 一.Vistumbler扫描器 WiFi 扫描器能能发现附近AP的详细信息,例如信号强 ...

  5. IO负载高的来源定位 IO系列

    http://elf8848.iteye.com/category/281637 前言: 在一般运维工作中经常会遇到这么一个场景,服务器的IO负载很高(iostat中的util),但是无法快速的定位到 ...

  6. Vc6&period;0打开该文件坠毁

    这是一个微软bug,下面是用户给出的溶液: http://blog.163.com/wjatnx@yeah/blog/static/12758622820138110530322/ http://bl ...

  7. 【转】Appium 服务器端从启动到case完成的活动分析

    原文地址:http://blog.csdn.net/zhubaitian/article/details/39474151 此文的目的主要是通过分析Appium Server打印出来的log,加深对A ...

  8. 抓包工具Wireshark的使用

    WireShark是非常流行的网络封包分析软件,功能十分强大.可以截取各种网络封包,显示网络封包的详细信息. WireShark界面简介 启动WireShark的界面如下: 选择网卡 wireshar ...

  9. unity导入TexturePacker处理

    1.从Asset Store里下载TexturePackerImporter ,然后导入到项目中. 2.导入unity的一张大图和一个.tpsheet文件(注意原始图片也要在相同目录) 3.代码导入 ...

  10. TDD、BDD、ATDD、DDD 软件开发模式

    TDD.BDD.ATDD.DDD 软件开发模式 四个开发模式意思: TDD:测试驱动开发(Test-Driven Development) BDD:行为驱动开发(Behavior Driven Dev ...