【设计模式】创建型-单例模式

《西游记》的美猴王孙悟空只能有一个,不可能存在多个孙悟空把?啥?你见过两个孙悟空?其中一个还把唐僧的行李抢走并打晕了,另一个在水帘洞!噢噢那不就是真假美猴王那一集么?俩猴子找谛听,观音,阎王都分辨不出真假,最终还是闹到如来佛祖那里了,最后假的也就是六耳猕猴变得,被孙悟空KO了。

通过这个故事在我们编程的领域,有很多场景就像美猴王一样,不可能存在多个,只能有一个,比如管理数据库的类,某个业务代码所属的类等等,也就是说你的系统中某个功能类需要唯一性的时候,这样的类,我们就要用到单例模式了。先看下不使用的代码:

我们建个美猴王类:

public class MonkeyKing {

    public void eye() {
        System.out.println("我会火眼金睛");
    }

    public void magic() {
        System.out.println("我会72变");
    }

}

然后再建两个神仙辨别真假美猴王:

public class Guanyin {

    private TrueAndFalseMonkeyKing.MonkeyKing monkeyKing1 = new TrueAndFalseMonkeyKing.MonkeyKing();
    private TrueAndFalseMonkeyKing.MonkeyKing monkeyKing2 = new TrueAndFalseMonkeyKing.MonkeyKing();

    public void distinguish() {
        System.out.println("猴子1技能");
        monkeyKing1.eye();
        monkeyKing1.magic();
        System.out.println("猴子2技能");
        monkeyKing2.eye();
        monkeyKing2.magic();
    }

}

public class Rulai {

    private TrueAndFalseMonkeyKing.MonkeyKing monkeyKing1 = new TrueAndFalseMonkeyKing.MonkeyKing();
    private TrueAndFalseMonkeyKing.MonkeyKing monkeyKing2 = new TrueAndFalseMonkeyKing.MonkeyKing();

    public void distinguish() {
        if(monkeyKing1 != monkeyKing2) {
            System.out.println("两个猴子不一样 其中一个必定是假");
        } else {
            System.out.println("是同一个猴子");
        }
    }

}

最后我们让如来和观音去辨别:

public static void main(String[] args) {
    Guanyin guanyin = new Guanyin();
    guanyin.distinguish();

    Rulai rulai = new Rulai();
    rulai.distinguish();
}

最终的结果可想而知,观音菩萨没有分辨出,而如来佛祖一下子就区分出来了。

我们把猴子改造一下,让他无论怎样都只有实例,并且把如来与观音创建猴子的地方改为getInstance() :

public class MonkeyKing {

    private static MonkeyKing instance;

    private MonkeyKing() {}

    public static MonkeyKing getInstance() {
        if (instance == null) {
           instance = new MonkeyKing();
        }
        return instance;
    }

    public void eye() {
        System.out.println("我会火眼金睛");
    }

    public void magic() {
        System.out.println("我会72变");
    }

}

public class Guanyin {

    private MonkeyKing monkeyKing1 = MonkeyKing.getInstance();
    private MonkeyKing monkeyKing2 = MonkeyKing.getInstance();

    public void distinguish() {
        System.out.println("猴子1技能");
        monkeyKing1.eye();
        monkeyKing1.magic();
        System.out.println("猴子2技能");
        monkeyKing2.eye();
        monkeyKing2.magic();
    }

}

public class Rulai {

    private MonkeyKing monkeyKing1 = MonkeyKing.getInstance();
    private MonkeyKing monkeyKing2 = MonkeyKing.getInstance();

    public void distinguish() {
        if(monkeyKing1 != monkeyKing2) {
            System.out.println("两个猴子不一样 其中一个必定是假");
        } else {
            System.out.println("是同一个猴子");
        }
    }

}

运行main函数之后,观音还是分辨不出,但是如来能分辨是不是同一只猴子了。

通过美猴王类的代码,我们能看到单例模式要求我们提供一个getInstance方法来返回唯一的对象,但是仅提供这个方法还不够,我们需要在方法前加上static关键字。这表明全局仅有一份getInstance方法,而构造器我们用私有的,就是防止用户不小心直接new出来对象造成该类会产生多个。当然,设计模式应该与语言无关,而关键字static并不是每种语言都有,像GO语言就没有,那怎么办?这些语言,一般都会存在全局变量,不像java,必须把变量约束在类中。要创建单例的对象,只能用锁机制去保护了。

说到锁,在java中也是很重要的,就像美猴王类,如果有多个线程去调用getInstance,那么就会产生数据竞争了,也可能会产生多个实例,所以我们就要加锁,防止多线程调用产生的数据竞争。

public static synchronized MonkeyKing getInstance() {
    if (instance == null) {
        instance = new MonkeyKing();
    }
    return instance;
}

我们在方法前面加上了synchronized关键字,这样就能保护它了。关于单例模式有好几种变体,都是与防止多线程竞争相关的。这里我就不再举例了,大家可以自行查找。

最后给出GOF的标准定义:确保一个类只有一个实例,并提供一个全局访问点来访问该实例。

demo地址:GitCode - 全球开发者的开源社区,开源代码托管平台

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Diros2025

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值