zoukankan      html  css  js  c++  java
  • 单例模式(Singleton)

    4.3 单例模式(Design Pattern:Singleton)

    单例模式属于创建型模式,目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。考虑这样一种对象,这个对象应该在程序启动时被创建,并且在结束时被删除,如应用程序的基础高层对象。通过这个对象可以得到系统中其他的对象,这些基础对象可能是前面提到的工厂对象(Factories),用来创建其他对象;也可能是管理器对象(managers),负责控管其他对象;或者是全局注册表(Registry)。类似这种类型的对象不该被创造出多份。
    4.3.1  单例模式的实现
    下面给出单例模式的实现。单例模式参与者如图4.8所示。其中Singleton定义了一个getInstance操作,允许客户访问它的惟一实例。可能负责创建它的惟一实例。
    图4.8  经典单例模式类图

    如图4.8和代码4.33,可以看到,经典单例模式的实现非常简单。而正是由于概念和实现上的简单,没有顾及到逻辑概念上、测试性、全局依赖、类装载器、序列化、线程安全以及不同JVM之间等等方面的问题,造成了很多的误用。
    代码4.33  Singleton.java
    package chapter4.pattern.singleton;
    public class Singleton {
      private static Singleton instance;
      /**
       * 不允许通过构造字来实例化
       */
      private Singleton() {
      }
      /**
       * 使用synchronized关键字,保障Singleton的线程安全
       */
      public synchronized static Singleton getInstance() {
        if (instance == null) {
          instance = new Singleton();
        }
        return instance;
      }
    }
    注意:Singleton模式应当谨慎使用。代码4.33中给出了一种合理的使用方法,通过将Singleton的构造子声明为私有,杜绝了外部或者子类的实例化倾向(当然这也是允许的)。
    通过为getInstance()方法签署synchronized锁,保障了Singleton是线程安全的。此处所指的线程不安全是指在多线程环境下,可能产生不同Singleton实例。另外,getInstance()方法实际上是一种静态工厂方法,这是工厂模式的一个变种。考虑上节中工厂模式的例子,尝试结合Factory和Singleton,如代码4.34所示。
    代码4.34  UtilServiceSingletonFactory.java
    package chapter4.pattern.singleton;
    import chapter4.pattern.factory.dipioc.ConcreteUtil;
    import chapter4.pattern.factory.dipioc.UtilService;
    public class UtilServiceSingletonFactory {
      private static UtilServiceSingletonFactory instance;
      /**
       * 允许子类实例化
       */
      protected UtilServiceSingletonFactory() {
      }
      public synchronized static UtilServiceSingletonFactory getInstance() {
        if (instance == null) {
          instance = new UtilServiceSingletonFactory();
        }
        return instance;
      }
      public UtilService make() {
        return new ConcreteUtil();
      }
    }
    客户代码可以是这样:
    UtilService utilService = UtilServiceSingletonFactory.getInstance().make();
    可以看到,通过单例工厂使得工厂对象实例数不超过1个,同时给出了Factory Method的实现(make方法),通过变换构造子修饰(private->protected),保留了Factory的派生性。如果不考虑派生性,完全有理由使用静态工厂而不是单例工厂,静态工厂使工厂类不需要被实例化,客户代码可以是这样:
    UtilService utilService = UtilServiceSingletonFactory.make();

    4.3.2  单例注册表
    接下来将要介绍Singleton模式的另一种常用实现,即单例注册表(Singleton Registry),如代码4.35~4.39所示。
    代码4.35  FactorySingletonRegistryUsage.java
    package chapter4.pattern.singleton;
    public class FactorySingletonRegistryUsage {
      public static void main(String[] args) {
        //实例化工厂注册表
        FactorySingletonRegistry registry = FactorySingletonRegistry.getInstance();
        //<- 通过反射机制,使得注册表可以依据给定的工厂全限定名返回具体工厂实例
        //第一次索取
        BeanFactory xmlBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory");
        BeanFactory listableBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory");
        //第二次索取
        BeanFactory xmlBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory");
        BeanFactory listableBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory");
        //->
        //比较先后两次索取的工厂实例,希望得到同一实例的工厂引用,结果正确
        System.out.println(xmlBeanFactory1.hashCode() == xmlBeanFactory2.hashCode());
        System.out.println(listableBeanFactory1.hashCode() == listableBeanFactory2.hashCode());
    }
    }
    代码4.36  FactorySingletonRegistry.java
    import java.util.HashMap;
    import java.util.Map;
    public class FactorySingletonRegistry {
      private static FactorySingletonRegistry instance;
      private static Map factoryMap = new HashMap();①
      private FactorySingletonRegistry() {
      }
      public synchronized static FactorySingletonRegistry getInstance() {
        if (instance == null) {
          instance = new FactorySingletonRegistry();
        }
        return instance;
      }

      public synchronized BeanFactory getBeanFactory(String factoryClassName) {②
        BeanFactory factory = (BeanFactory)factoryMap.get(factoryClassName);
        if (factory != null) return factory;
        try {
         factory = (BeanFactory)Class.forName(factoryClassName).newInstance();③
        } catch (ClassNotFoundException e) {
          System.out.println("Couldn't find class " + factoryClassName);
        } catch (InstantiationException e) {
          System.out.println("Couldn't instantiate an object of type " + factoryClassName);
        } catch (IllegalAccessException e) {
          System.out.println("Couldn't access class " + factoryClassName);
        }
        factoryMap.put(factoryClassName, factory);
        return factory;
      }
    }
    代码4.37  BeanFactory.java
    public interface BeanFactory {
    }
    代码4.38  ListableBeanFactory.java
    public class ListableBeanFactory implements BeanFactory {
      public ListableBeanFactory() {
        System.out.println("ListableBeanFactory Created");
      }
    }
    代码4.39  XmlBeanFactory.java
    public class XmlBeanFactory implements BeanFactory {
      public XmlBeanFactory() {
        System.out.println("XmlBeanFactory Created");
      }
    }
    对单例注册表的简单运作方式,做如下说明:
    (1)请看代码4.36,首先这个类是一个单例类,在①处使用了Map对象,这是一个类,持有聚集的讯息。所谓聚集通常就是在Map中以名值对(field-value)的形式存储一系列各类对象的实例引用,通过put(field,value)存储,通过get(field)取得value。在代码4.36的getBeanFactory方法中,通过对factoryMap的存取,可以使该单例注册表持有任意数量的工厂实例,并且通过if (factory != null)的判断,保证了返回的是对同一个工厂实例的引用。
    (2)注意代码4.36中的②处,getBeanFactory(String factoryClassName)是一个参数化的工厂方法,和在代码4.26中的类似。
    说明:如果工厂方法依赖一个参数标识来决定产品的具体生产行为,那么可以说这就是一个参数化工厂方法。参数化工厂的好处是,创建同一产品族系(拥有同一接口的具体产品)时,不再需要衍生具体的工厂子类来对应。主要缺点是工厂职责过于集中,另外一个缺点仍然是所有传统工厂模式的通病,就是由于硬编码的关系,需要使用if/else语句来决定生产方式,无法摆脱Product = new ConcreteProduct()这种代码带来的强依赖性,同时限制了工厂创建产品的种类数目。这也意味着每当引入新的产品时,就需要重新变更关联代码。
    (3)在代码4.36中引入了反射(Reflection)技术,如③所示。在代码4.35中,可以以类的全限定名作为参数传入工厂方法,工厂方法会通过反射技术来实例化那个类,而不需要预先在工厂方法中记载有限的产品集合了。
    至此,给出了单例模式的基本用法,单例模式是一种常用的模式,但也很容易被误用。最后还提到了反射和工厂方法模式的一些结合,这是非常有用的技术。

    转自:http://book.csdn.net/bookfiles/92/100922653.shtml

  • 相关阅读:
    通过 curl 命令访问 K8S API
    k8s 调度 Affinity
    golang 定期发送 RA 报文
    Ticker 使用
    查看 host/container veth pair 关系
    Kubernetes 服务 service 的负载均衡分析
    698 TypeScript泛型的使用:泛型实现类型参数化,泛型接口,泛型类,泛型约束
    697 TypeScript接口的使用:接口的声明,可选属性,只读属性,索引类型,函数类型,接口继承,交叉类型,接口的实现,字面量赋值,枚举类型
    696 TypeScript类的使用:类的定义,继承,多态,成员修饰符,readonly,getters/setters,静态成员,抽象类abstract,抽象类演练,类的类型
    695 TypeScript函数类型:可选参数,默认参数,剩余参数,this类型,函数的重载,联合类型和重载
  • 原文地址:https://www.cnblogs.com/kelin1314/p/1369891.html
Copyright © 2011-2022 走看看