设计模式----单例模式
更新日期:
创建型模式
单例模式是某个类只需要一个实例,保证一个类有且只有一个实例,并提供一个访问他的全局访问点。比如对于一个统一的数据库的访问,在整个项目中只使用同一个实例。对于这种情况有个比较好的例子,就是一夫一妻制。
比如某个男子需要娶个女子结婚,那么就有下面的程序:wife类,代表女子,husband类,代表男子
public class Wife {
public void show() {
System.out.println("the girl show themself");
}
public void marry() {
System.out.println("the girl gets married");
}
}
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类修改如下:
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类:
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前加锁
lock.lock();
if (wife == null) {
wife = new Wife();
}
lock.unlock();
这样就能保证在A实例化之前B不会进行null的判断。这样虽然满足了功能的要求,可是每次调用的getInstance的时候都要进行加锁工作,这将会影响程序的性能。所以对其进行改良。
public static Wife getInstance(){ //静态方法来实例化
if (wife == null) {
lock.lock();
if (wife == null) {
wife = new Wife();
}
lock.unlock();
}
return wife;
}
在临界区内在进行一次null的判断,只有wife为null的时候才会进入初始化。
单例模式本身的代码:
public class Singleton {
public static Singleton singleton; //静态变量
private Singleton(){
//私有构造方法,是的外部无法访问
}
public static Singleton getInstance(){ //静态方法来实例化
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
客户端代码:
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个请求同时方式这个类的实例的时候,可以会在同一时间点上都创建一个实例,虽然一般不会出异常错误,但是起码不是我们谈论的只保证一个实例了。每一个设计模式都值得我们深入的研究下去,有时间一定回头看看。