• 设计模式系列(一)单例模式


    一、用通俗的语言解释其意义:从秦始皇之后确立了皇帝的位置,且同一时期只有一个。因此大家在称呼的时候不需要为皇帝加上其他的前缀。这一过程反应在设计领域就是,要求一个类只能生成一个对象,所有对象对他的依赖都是相同的,因为只有一个对象,所以对象对他的依赖都是相同的,因为只有一个对象,大家对他都很了解。皇帝每天要处理很多的事情,但是皇帝只有一个,即一个类只有一个对象,对象产生通过new关键字完成,我们可以使用构造函数来控制,因为使用new关键字创建对象时都会根据输入的参数调用相应的构造函数,所以如果我们将构造函数的访问权限设置为private就可以进制外部创建对象了。

    将以上的情景设置为简单的例子:

    皇帝类:

     1 public  class Emperor{
     2 private static final Emperor emperor=new Emperor();//初始化一个皇帝
     3 private Emperor(){
     4 }
     5 public static Emperor getInstance(){
     6 return emperor;
     7 }
     8 public static void say(){
     9 System.out.println("我的就是皇帝");
    10 }
    11 }

    通过定义私有的访问权限的构造函数,避免被其他类new出一个对象,而Emperor自己则可以new一个对象处理,其他类都可以访问getInstance获得同一个对象。

    大臣类:

    1 public class Minister{
    2 public static void main(String[]args){
    3 for(int day=0;day<3;day++){
    4 Emperor emperor=Emperor.getInstance();
    5 emperor.say();
    6 }
    7 }
    8 }


    二、单例模式的定义

    单例模式(Singleton Pattern):Ensure class has only one instance,and provide a global point of access to it.

    Singleton类称为单例类,通过使用private的构造函数确保在一个应用中只产生一个实例。并且是自己实例化的(在Singleton中使用new Singleton)。

    单例模式的通用代码:

    1 public class Singleton{
    2 private static final Singleton singleton=new Singleton();
    3 private Singleton(){}
    4 public static Singleton getSingleton(){
    5 return singleton;
    6 }
    7 public static void doSomething(){}
    8 }

    三、单例模式的应用
    1、单例模式的优点

      由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的创建和销毁时,而且创建或销毁的性能又无法优化,单例模式的优势就非常明显。

      由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

      单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。

      单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

    2、单例模式的缺点

      单例模式一般没有接口,扩展比较困难,若要扩展,除了修改没有第二个办法。单例模式为什么不能增加接口?因为接口对单例模式是没有任何意义的,他要求自行实例化,并且提供单一实例。接口或抽象类是不能被实例化的。

      单例模式对测试是不利的,在单例模式没有完成之前,是不能进行测试的。

      单例模式与单一职责原则是有冲突的,一个类应该只实现一个逻辑,而不关心她是否单例,是不是单例要取决于环境,单例模式将要单例和业务逻辑融合在一个类里。

    3、单例模式的使用场景

       要求生成唯一序列号的环境;

       在整个项目中需要一个共享访问点或共享数据,例如一个web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式爆出加护器的值,并确保线程安全。

       创建一个对象需要消耗资源过多,如要访问IO和数据库等资源

       需要定义大量的静态常量和静态方法如工具类等。

    4、单例模式的注意事项

    在高并发的情况下,要注意单例模式的线程同步问题

    线程不安全的单例:

     1 public class Singleton{
     2 public static Singleton singleton=null;
     3 private Singleton(){}
     4 public static Singleton getSingleton(){
     5 if(singleton==null){
     6 singleton=new Singleton();
     7 }
     8 return singleton;
     9 }
    10 }

    该单例模式在低并发的情况下是没有问题的,在并发增多时,则可能在内存中存在多个实例,破坏最初的预期。例如线程A执行到singleton=new Singleton()但是没有获得对象,此时第二个线程也开始判断平执行,解决线程不安全的方法很多,可以在getSingleton方法前面加上synchronized来实现;还要考虑对象的复制情况。在java中默认是不可以复制的,若实现的Clonable接口,并实现了clone方法则可以直接通过复制方式创建一个新对象,对象复制是不调用构造函数的,因此即使是私有构造函数依然可以复制。一般很少单例类会主动要求被复制,因此不要实现Clonable接口。
    四、单例模式的扩展

    我们之前看到的都是单例创建单一对象。如果要多于一个对象该如何。

    固定数量的皇帝类:

     1 public class Emperor{
     2 //定义最多能产生的实例实例
     3 private static int maxNumOfEmperor=2;
     4 //每个皇帝都有名字,使用一个ArrayList来容纳,每个对象的私有属性
     5 private static ArrayList<String> nameList=new ArrayList<String>();
     6 //定义一个列表,容纳所有的皇帝实例
     7 private static ArrayList<Emperor> emperorList=new ArrayList<Emperor>();
     8 //当前皇帝序列号
     9 private static int countNumOfEmperor=0;
    10 //产生所有的对象
    11 static{
    12 for(int i=0;i<maxNumOfEmperor;i++){
    13 emperorList.add(new Emperor("皇"+i+"帝"));
    14 }
    15 }
    16 private Emperor(){
    17 }
    18 private Emperor(String name){
    19 nameList.add(name);}
    20 public static Emperor getInstance(){
    21 Random random=new Random();
    22 countNumOfEmperor=random.nextInt(maxNumOfEmperor);
    23 return emperorList.get(countNumOfEmperor);
    24 }
    25 public static void say(){
    26 System.out.println(nameList.get(countNumOfEmperor));}
    27 
    28 }

    大臣类:

     1 public class Minister{
     2 public static void main(String[]args)
     3 {
     4 int ministerNum=5;
     5 for(int i=0;i<ministerNum;i++){
     6 Emperor emperor=Emperor.getInstance();
     7 System.out.println("第"+(i+1)+"个达成拜的是:");
     8 emperor.say();
     9 }
    10 }
    11 }


    这种也叫作有上限的多例模式。

    五、实践

    在Spring中。每个Bean默认就是单例的,这样做的优点是Spring容器可以管理他们的生命周期,创建 销毁等。如果没有采用非单例模式。则Bean初始化之后的管理交由J2EE容器,Spring容器不再跟踪管理Bean的生命周期。

     

  • 相关阅读:
    mysql 刘道成视频教程 第4-8课 --- 数据类型
    mysql 刘道成视频教程 第3课
    9款优秀的开源版本控制和源代码管理系统 转载
    mysql主要应用场景 转载
    平时收藏网页
    mysql 刘道成视频教程1、2课----------大致结构
    软件
    visual studio 2010 快捷键
    将CString(unicode)转换为char*(ANSI)
    去掉Visual Studio 编辑器里中文注释的红色波浪线 转载
  • 原文地址:https://www.cnblogs.com/dream-to-pku/p/5754720.html
Copyright © 2020-2023  润新知