《西游记》的美猴王孙悟空只能有一个,不可能存在多个孙悟空把?啥?你见过两个孙悟空?其中一个还把唐僧的行李抢走并打晕了,另一个在水帘洞!噢噢那不就是真假美猴王那一集么?俩猴子找谛听,观音,阎王都分辨不出真假,最终还是闹到如来佛祖那里了,最后假的也就是六耳猕猴变得,被孙悟空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的标准定义:确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
2618

被折叠的 条评论
为什么被折叠?



