`

创建模式之单例、原型

阅读更多

      在创建型模式中,单例(Singleton)模式和原型(Prototype)模式相对来说其用意更为简单明了。单例(Singleton)模式确保某类只有一个实例,且自行实例化并向整个系统提供这个实例;原型(Prototype)模式通过给出一个原型对象来指明所要创建的对象类型,并通过Clone的方式创建出所需的同类型的对象。接下来,我们针对这两种模式的一些常见问题给出简单阐述。

      #单例(Singleton)模式

            #特点:1、只能有一个实例;2、必须自行创建这个实例;3、必须自行向整个系统提供这个实例;4、因构造方法似有,不可被继承。其中,1是通过构造方法来保障的,2&3是通过工厂方法来实现的。
            #使用单例的必要条件(使用场景):一个系统要求一个类只有一个实例时。

            #实现方式及其存在特点:在上篇博文我们曾提到过,单例(Singleton)模式常被拿来作为笔试考察的,侧重于单例模式的实现(所谓的饿汉、懒汉、注册、双重检查等实现方式优劣以及多类加载器、多JVM情况下Singleton的表现)及其使用场景。本篇博文将着力解决这几个问题。

             #实现方式之饿汉:java语言里实现起来最为简便的单例(Singleton),下面是其示意图及其实现代码。

             #实现方式之懒汉:与饿汉相比,相同之处为构造方法均为私有(类不能被继承),不同之处为饿汉在加载时被实例化,采用了静态工厂方法提供自身唯一的实例;而懒汉是在第一次调用时被实例化,采用了同步化的静态工厂方法提供自身唯一的实例。下面是其示意图及其实现代码。

            #饿汉与懒汉特点:其一私有构造子使其均无法被继承,其二加载方式带来的在资源利用效率与反应速度上的差异性。饿汉式单例在自己加载时就将自己实例化,即便加载器是静态的。仅从资源利用效率角度来讲,这个比懒汉式单例稍差点,但从时间的反应速度角度来讲,则比懒汉式单例稍好些。懒汉式单例在实例化时,必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器在实例化时必然涉及资源初始化,而资源初始化可能耗费时间,这意味着出现多线程同时首次引用此类的几率变得较大。

            注:饿汉单例类可以在java语言内实现,但不易在C++内实现,因为静态初始化在C++中没有固定顺序,因而静态变量的初始化与类的加载顺序没有保证,可能会出问题。这便是为何GOF在提出单例概念时,举得例子是懒汉式的。因其影响力之大,以致java语言中单例类的例子大多是懒汉式。实际上,对于java,推荐饿汉式。

            #实现方式之注册:是GOF为了克服以上两种方式实现的单例均无法继承的缺点而设计的。顺便提下,一般情况下这种方式并不是十分必要的,它只是提供了一种单例需求的实现方式,但不能保证单例,因为它开放了构造方法的保护等级,在这里仅做一般了解即可。不过,这个模式却给我了一个多例的实现思想,所谓多例,即允许n(n>1)个实例而不单单一个实例,但我们平常用到较多的类似于多例的是池的概念,两者的不同之处在于池强调可用对象的量,而多例在于强调量与构造方法的私有。下面示意图的代码实现上,基类采用了饿汉方式,但其子类的实例化只能是懒汉式的,这是无法改变的。

             #实现方式之双重检查成例(java中不可实现)

                   我们说过,在上面给出的懒汉式单例实现里对静态工厂方法使用了同步化,以处理多线程环境。有些设计师建议这里使用所谓的“双重检查成例”,之所以提到它,是因为它在C语言中得到了普遍的应用,然而,需要指出的是,这是不可以在java中使用的。我们首先来看一下,java中“双重检查成例”单例的实现:

package pattern.creational.singleton;
/**
*这是一个反面的错误例子
*/
public class LasySingleton {
   private static LasySingleton instance = null;

   private LasySingleton() {}

   public static LasySingleton getInstance() {
       if(instance == null) {
           synchronized(LasySingleton.class) {
               if(instance == null) {
                   instance = new LasySingleton();
                }
            }
	}
       return instance;
   }

   public void aService() {//do something}
} 

              该实现的初衷在于降低懒汉式单例方式同步化的开销,因为懒汉式初始化有效的仅为首次调用,但为了适应多线程环境,不得不同步化静态工厂方法,无疑加大了额外开销,若能缩小同步化范围(如上面的同步化代码块),可取得较佳的效果。上面的方式的确仍然可以保证只有一个单例类实例存在,但美好的初衷,依旧无法在java中得到满足。接下来我们再看两个问题:

              问题一:画蛇添足。 上面的这个技巧,第一次或第二次检查是否可以省略掉?答案是“否”:按照多线程原理和双重检查成例预想方案,它们是不可以省略掉的。

              问题二:“双重检查成例”懒汉单例为何在java语言中不成立(而对C语言成立)?答案是:双重检查成例对java语言编译器不成立。在java编译器中,LasySingleton类的初始化与instance变量赋值顺序是不可预料的。若一个线程在没有同步化的条件下读取instance引用,并调用这个对象的话,可能会发现对象的初始化尚未完成,从而造成崩溃。(比较怪异,个人认为,但不得不遵循最佳实践

              #实现方式之最佳实践:到目前为止,人们得出的结论是:一般而言,双重检查成例无法在现有的java语言编译器里工作(Joshua Bloch. Effectiv Java-Programming Language Guide. published by Addison-Wesley,2001)。一般情况下,单例模式可采用饿汉式单例模式或对整个静态工厂方法同步化的懒汉式单例模式。

             #单例模式在下列环境的局限性

                #局限性1:多个JVM系统的分布式系统:EJB容器有能力将一EJB的实例跨过多个JVM调用。因Singleton不是EJB型,故Singleton只局限于某一JVM中。也就是说,若EJB在跨过多个JVM后仍然需要引用同一个Singleton类的话,这个Singleton就会在数个JVM中被实例化,造成多个Singleton对象的实例出现。一个J2EE系统可能分布在数个JVM中,这时不一定需要EJB,就能造成多个Singleton的实例出现在不同的JVM中。若Singleton是无状态的,便没问题。应当注意,在任何使用了EJB、RMI和JNI技术的分布式系统中,应当避免使用有状态的Singleton模式。

                 #局限性2:多个类加载器:同一个JVM中会有多个类加载器,当两个类加载器同时加载一个类时,会出现两个实例。很多J2EE服务器允许同一个服务器内有几个servlet引擎时,每个引擎都有独立的类加载器,经由不同的类加载器加载的对象之间是绝缘的。比如,一个J2EE系统所在的J2EE服务器中有两个Servlet引擎:一个作为内网给公司的网站管理人员使用,另一个给公司的外部客户使用。两者共享同一个数据库,两个系统都需要调用同一个单例类。若该单例类是有状态的话,那么,内网和外网用户看到的单例类对象的状态就会不同。除非系统有协调机制,不然在这种情况下应当尽量避免使用有状态的单例类。

 

...待续...

0
0
分享到:
评论

相关推荐

    白话讲解创建型设计模式:单例原型构建

    本文将聚焦于创建型设计模式,特别是单例模式和原型模式,这两种模式在对象的创建方面扮演着重要的角色。 单例模式是一种限制类实例化次数的设计模式,确保在整个应用程序中,一个类只有一个实例存在。这在某些场景...

    设计模式:创建型之原型(深浅拷贝)、单例(饿汉式与懒汉式),三大工厂模式 结构型之适配器模式,代理模式(三大代理)

    创建型模式关注的是对象的创建过程,包括单例模式、原型模式、工厂模式等。单例模式确保一个类只有一个实例,并提供一个全局访问点;原型模式则是通过复制现有的实例来创建新的实例,适用于创建复杂对象或者避免重复...

    常用设计模式特点比如工厂模式、单例模式等等

    - "对象创建"模式:如建造者模式、原型模式,它们专注于对象的创建过程,允许更复杂的实例化逻辑。 - "对象性能"模式:例如享元模式,通过共享对象来减少内存消耗,提高性能。 - "接口隔离"模式:提倡接口的职责应当...

    设计模式入门之一:深入单例模式

    单例模式属于创建型设计模式,还有其他如抽象工厂、工厂方法、建造者、原型模式等,它们都关注于对象的创建。结构型模式如组合、门面、代理、适配器、装饰者、桥接、享元模式则关注于如何组织和组合类或对象。行为型...

    设计模式之原型模式.docx

    【设计模式之原型模式】 设计模式是软件工程中的一种最佳实践,是对在特定上下文中反复出现的软件设计问题的解决方案。原型模式属于对象创建型模式,它的主要思想是通过复制已有对象来创建新对象,降低了类的实例化...

    C#23种设计模式样例代码和UML图

    创建型模式(抽象工厂模式、工厂方法模式、单例模式、建造者模式、原型模式); 行为型模式(策略模式、 迭代器模式、原型模式、职责链模式、 模板方法、 命令模式、 解释器模式、 中介者模式、 访问者模式、 状态...

    实验六:原型模式.rar

    单例模式中,有时也会用到原型模式来创建单例的副本。 总的来说,实验六“原型模式”是一个很好的学习和实践设计模式的机会,它能帮助开发者更好地理解和运用Java的克隆机制,以及在实际项目中如何有效地利用原型...

    设计模式之原型模式

    原型模式(Prototype Pattern)是设计模式中的一种结构型模式,它的主要目的是通过克隆已有对象来创建新对象,而不是重新创建。这种模式可以减少代码重复,提高效率,并且在某些场景下,比如对象的创建成本较高或者...

    设计模式:简单工厂、方法工厂、抽象工厂、单例、原型、委派、模板、代理、策略

    5. **原型**:原型模式是一种创建型设计模式,它通过复制已有对象来创建新对象,避免了复杂的初始化过程或重复的创建开销。 6. **委派**:委派模式是一种行为设计模式,它将请求从一个对象传递给另一个对象,让接收...

    精品源码 C# 27种设计模式源代码

    2 单例模式 2 门面模式 2 模板方法模式 3 策略模式 3 工厂方法模式 3 组合模式 4 抽象工厂模式 4 代理模式 4 命令模式 5 迭代器模式 5 适配器模式 5 原型模式 6 简单工厂模式 6 解释器模式 6 享元模式 7 桥接模式 7 ...

    软件设计模式之创建模式例子

    压缩包中的“创建模式”文件可能包含了对以上各种模式的详细解释、示例代码以及实际应用案例,对于理解并掌握这些创建型设计模式有着重要的参考价值。通过学习和实践,开发者可以更好地设计出可扩展、易维护的系统。

    设计模式之创建型模式实验.docx

    【设计模式之创建型模式实验】 创建型设计模式主要关注对象的创建,它们提供了一种在不指定具体类的情况下创建对象的方式,增强了系统的灵活性和可扩展性。在本实验中,我们将探讨五种常见的创建型设计模式:简单...

    设计模式之创建型模式.pdf

    创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式等。这些模式各有其特定的应用场景和优势。 首先,单例模式是一种常见的创建型模式,它保证一个类只有一个实例,并提供一个全局访问点。...

    JAVA设计模式(创建模式 结构模式 行为模式)

    本资源主要涵盖了创建模式、结构模式和行为模式三大类,每种模式都是面向对象设计的核心部分。 创建模式主要用于解决对象的创建问题,使得对象的创建过程更加灵活,易于扩展。其中: 1. 工厂模式(Factory Pattern...

    23种设计模式详解PDF

    、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式(7): 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式(11): 策略模式、模板方法模式、观察者模式、...

    Java面试 java设计模式整理 单例、工厂、策略等 面试使用,学习使用

    原型模式通过复制已有对象来创建新对象,减少了类的实例化次数,尤其适用于创建大型对象时。 6. **适配器模式(Adapter)**: 适配器模式让两个不兼容的接口能够协同工作,通过包装一个类的接口以匹配另一个接口...

    设计模式-C++

    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:...

    java 23种设计模式.zip

    1.创建型模式:工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式。 2.结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。 4.行为型模式:模板方法模式、命令模式、...

Global site tag (gtag.js) - Google Analytics