• 设计模式----单例模式


    创建型模式

    单例模式是某个类只需要一个实例,保证一个类有且只有一个实例,并提供一个访问他的全局访问点。比如对于一个统一的数据库的访问,在整个项目中只使用同一个实例。对于这种情况有个比较好的例子,就是一夫一妻制。

    比如某个男子需要娶个女子结婚,那么就有下面的程序:wife类,代表女子,husband类,代表男子

    1
    2
    3
    4
    5
    6
    7
    8
    public class Wife {
        public void show() {
            System.out.println("the girl show themself");
        }
        public void marry() {
            System.out.println("the girl gets married");
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Husband {
     
        private Wife myWife = null;
        public void chooseGirl() {
                    myWife = new Wife();
            myWife.show();
        }
        public void getMarry() {
            myWife.marry();
        }
    }

    在主程序里就可以实例化husband类,然后依次调用chooseGirl()和getmarry()方法,就可以正常运行。但是如果先调用getMarry()呢?由于wife在choose里面实例化的,这是就会报错,将husband类修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void chooseGirl() {
        if (myWife == null) {
            myWife = new Wife();
        }
        myWife.show();
    }
    public void getMarry() {
        if (myWife == null) {
            myWife = new Wife();
        }
        myWife.marry();
    }

    这样一来就可以保证一个丈夫只能有一个妻子了。而且两个函数没有调用的先后关系。

    问题一:如果婆婆想要看儿媳妇呢?那就会有一个婆婆类,婆婆类里面又需要实例化一个wife类,这个实例化的wife类和husband的里面实例化的wife类是不同的,出现了多个实例化的问题,也就是婆婆看到的和儿子娶的不是同一个姑娘,这怎么行呢?这就需要修改wife类的代码,保证只有一个实例出现。wife类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Wife {
        public static Wife wife;  //静态变量
        private Wife(){          //私有构造方法,是的外部无法访问
     
        }
        public static Wife getInstance(){  //静态方法来实例化
            if (wife == null) {
                wife = new Wife();
                    }
            return wife;
        }
        public void show() {
            System.out.println("the girl show themself");
        }
        public void marry() {
            System.out.println("the girl gets married");
        }
    }

    将构造函数设为私有,声明静态变量wife和静态方法getinstance来进行实例化。在需要实例化的地方用Wife wife=Wife.getInstance()来获得实例,就可以保证大家看到的是同一个姑娘。

    问题二:如果婚礼现场需要直播,怎么办?可以使用多线程技术,一个线程A作为实地的婚礼,在wife中调用marry方法,另一个线程B作为直播,同样在线程中调用marry的,那么怎么能确定getInstance得到的是同一个新娘呢?如果线程A先运行到此,这是wife为null,那么进入到分支实例化wife,在未完成实例化的时候,线程B也来到了分支内,此时wife依然为null,线程B又会进行一次实例化,最终还是产生了两个实例,就是两个不同的新娘。

    解决:引入锁的概念,lock的作用就是构造出一块临界区来控制线程对代码的访问,确保每次只有一个线程运行到临界区的代码,其他线程运行到临界区时,将会一直等待直到前面的线程运行出临界区为止。

    在wife中修改代码:在if前加锁

    1
    2
    3
    4
    5
    lock.lock();
    if (wife == null) {
        wife = new Wife();
    }
    lock.unlock();

    这样就能保证在A实例化之前B不会进行null的判断。这样虽然满足了功能的要求,可是每次调用的getInstance的时候都要进行加锁工作,这将会影响程序的性能。所以对其进行改良。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static Wife getInstance(){  //静态方法来实例化
            if (wife == null) {
                lock.lock();
                if (wife == null) {
                    wife = new Wife();
                }
                lock.unlock();
            }
            return wife;
        }

    在临界区内在进行一次null的判断,只有wife为null的时候才会进入初始化。

    单例模式本身的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Singleton {
        public static Singleton singleton;  //静态变量
        private Singleton(){ 
            ^&&&                //私有构造方法,是的外部无法访问  
        }
        public static Singleton getInstance(){  //静态方法来实例化
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    客户端代码:

    1
    2
    3
    4
    5
    Singleton singleton1 = Singleton.getInstance();
    Singleton singleton2 = Singleton.getInstance();
    if(singleton1 == singleton2){
        System.out.printle("the teo object are the same one");
    }

    单例模式虽然很简单,但很实用,以下情况都适合实用:

    1)当类只能有一个实例而且第三方可以从一个公共的访问点访问它时

    2)当一个唯一的实例可以通过子类化来扩展,而且第三方需要在不更改代码的情况下就能实用一个扩展的实例时。

    单例模式优点:

    1)队唯一的实例做出访问控制

    2)允许改变实例的个数,可以增加一个计数器来控制实例的个数,从而有双例模式,三利模式等。

    总结:

    单例并不是上面写的这么简单,里面涉及到很多的知识点,多线程、加锁、私有构造函数,静态构造函数,静态字段,readonly和const的区别等等。

    向这里给出的单例版本就涉及到线程安全的问题,当2个请求同时方式这个类的实例的时候,可以会在同一时间点上都创建一个实例,虽然一般不会出异常错误,但是起码不是我们谈论的只保证一个实例了。每一个设计模式都值得我们深入的研究下去,有时间一定回头看看。

  • 相关阅读:
    JDBC
    Maven入门初级教程
    os.path路径拓展 python3
    requests实现文件下载, 期间显示文件信息&下载进度_python3
    yield浅析-Python3
    Scoop
    U盘启动盘制作工具(安装Linux)
    JavaScript摘要笔记
    Hexo+Github搭建博客&各种设置
    Linux下搭建svn服务端
  • 原文地址:https://www.cnblogs.com/silence-hust/p/4127294.html
Copyright © 2020-2023  润新知