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

       单例模式是一种常见的设计模式,其核心为“每个类只能有一个实例”。通过对类中对象实例化的控制,确保某些场景下状态的唯一性。在系统中的某些类只有一个实例非常重要,如打印任务、一个窗口管理器或文件系统等。

      背景

           在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。例如:系统中可以存在多个打印任务,但只能用一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器;一台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况;一台计算机可以有若干通信端口,但是系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用等。因此,单例模式可以被广泛的应用的下述环境中:

    • 要求生成唯一序列号的环境;
    • 整个项目中需要一个共享访问点或共享数据。如,一个web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,确保线程安全;
    • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
    • 需要定义大量的静态常量和静态方法(如工具类)的环境,采用单例模式(或static声明);

    一、定义

          单例模式(Singleton Pattern)即某各类中只有一个实例,且自行实例化并向整个系统提供这个实例。

    二、特点

    • 单例类只能有一个实例。
    • 单例类必须自己自己创建自己的唯一实例。
    • 单例类必须给所有其他对象提供这一实例。

    三、实现

    举例:一个国家只能有一个皇帝(即单例类);

    3.1 单例模式通用

    例1:
    /* 单例模式通用代码*/
    
    package simplePattern;
    
    public class Singleton {
        //定义各一个单例类
        private static final Singleton singleton = new Singleton();
        //定义私有的构造函数,限制一个类只能产生一个对象
        private Singleton(){
            
        }
        public static Singleton getSingleton(){
            return singleton;
        }
        //类中其他方法能够其他类调用
        public static void doSomething(){
            
        }
    }

    然而,单例模式也存在一些缺点。如:

    • 一般没有接口,扩展困难;
    • 不利于测试。在并发环境中,如果单例模式没有完成,不能进行测试,没有接口也无法使用mock的方式虚拟一个对象。
    • 单例模式与单一职责原则有冲突。单例模式中把“要单例”和业务逻辑融合在一个类中

    因此,针对具体情况可以对单例模式进行改进。

    3.2 高并发下单例模式实现

    例2:
    public class Singleton_hignconcur {
        private static Singleton_hignconcur singleton= null;
        //定义私有的构造函数,限制一个类只能产生一个对象
        private Singleton_hignconcur(){
            
        }
        //方法
        public static Singleton_hignconcur getSingleton(){
            if(singleton==null){
                singleton=new Singleton_hignconcur();
            }
            return  singleton;
        }
    }

      然而,上述代码实现可能存在线程A执行到singleton = new Singleton(),线程B则依然执行到(singleton == null)判断时依然为真,此时,由于A尚未返回实例判断条件仍未null,此时可能出现2个对象,因此线程不安全。

      解决上述线程不安全的方法一般有2种,(1)在getSingleton()方法前添加synchronized关键字,或在该方法内添加synchronized关键字,也可使用饿汉式单例(例1)或懒汉式单例模式(例2);(2)考虑对象复制的情况。在JAVA中对象默认不能复制,若实现了Cloneable接口,并实现clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制不需要调用类的构造函数,因此,即使是私有的构造函数,对象仍可被复制。因此,尽量使单例类不实现Cloneable接口。

    4. 单例模式扩展

       一个类需要多个对象时,直接使用new关键字即可,只能有一个对象时,使用单例模式,但对于要求某各类只能有n(n可为任一固定自然数)个对象时,如何实现?

    public class Emperor {
        //定义最多能产生的对象数量
            private static int maxNum=3;
            //每个对象(皇帝)拥有的属性(姓名),使用list存储
            private static ArrayList<String> nameList = new ArrayList<String>();
            //定义一个列表,容纳所有的对象实例
            private static ArrayList<Emperor> emperorList= new ArrayList<Emperor>();
            //定义当前皇帝的序号
            private static int order=0;
            //产生所有对象
            static {
                for(int i=0;i<maxNum;i++){
                    emperorList.add(new Emperor("皇帝"+(i+1)));
                }
            }
            private Emperor(){
                //存在约束条件(世俗和道德),使得不产生第二个皇帝
            }
            //传入皇帝名称,建立一个对象
            private Emperor(String name){
                nameList.add(name);
            }
            //随机获得一个皇帝实例
            public static Emperor getInstance(){
                Random ran=new Random();
                order= ran.nextInt(maxNum);
                return emperorList.get(order);
            }
            //方法
            public static void say(){
                System.out.println(nameList.get(order));
            }
    }
    public class Minister {
        public static void main(String[] args){
            //定义5个大臣
            int maxNum=5;
            for(int i=0;i<=maxNum;i++){
                Emperor emp=Emperor.getInstance();
                System.out.println("第"+(i+1)+"个臣子的是:");
                emp.say();
            }
        }
    }

    运行结果如下:

      这种需要产生固定数量对象的模式称作有上限的多例模式,是单例模式的一种扩展。采用多例模式可以在设计时觉得在内存中有多少个实例,方便系统进行扩展,修正可能存在的性能问题,提供系统响应速度。

    参考《大话设计模式》与《设计模式之禅》

  • 相关阅读:
    WF编译报错
    VS2012编译错误信息,错误列表却没显示
    SQL Server带游标的SQL
    SQL Server创建LinkServer
    ASP.NET自定义控件加载资源WebResource问题
    sqlserver 增加用户并分配权限
    Java for C#程序员
    laravel安装
    Convert Geometry data into a Geography data in MS SQL Server
    linux安装ruby
  • 原文地址:https://www.cnblogs.com/mo-lu/p/10274144.html
Copyright © 2011-2022 走看看