当前位置: 移动技术网 > IT编程>软件设计>设计模式 > 设计模式之单件模式

设计模式之单件模式

2019年08月10日  | 移动技术网IT编程  | 我要评论
今天我们来看一下单件模式,这个模式是所有模式中类图最简单的哦! 为什么用单件模式: 有些对象我们只需要一个,比如:连接池、缓存、对话框、和注册表对象、日志对 象等对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许 多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果。 ...

今天我们来看一下单件模式,这个模式是所有模式中类图最简单的哦!

为什么用单件模式:

           有些对象我们只需要一个,比如:连接池、缓存、对话框、和注册表对象、日志对

           象等对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许

           多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果。也就

          是为了防止多次 new 对象。

从一个简单的单件模式入门:

 

 1 public class singleton {
 2     private static singleton uniqueinstance;
 3  
 4     // other useful instance variables here
 5  
 6     private singleton() {}
 7  
 8     public static singleton getinstance() {
 9         if (uniqueinstance == null) {
10             uniqueinstance = new singleton();
11         }
12         return uniqueinstance;
13     }
14  
15     // other useful methods here
16     public string getdescription() {
17         return "i'm a thread safe singleton!";
18     }
19 }

 

在这里主要注意的点有:

               第二行:利用一个静态变量来记录singleton类的唯一实例;

               第六行:把构造器声明为私有的,只有字singleton类内才可以调用构造器;

               第八行至第十三行:用getinstance()方法实例化对象并返回这个示例(如果

                                             uniqueinstance是空的则利用私有构造器产生一个sin-

                                             gleton实例,否则表示已经有了实例,并将uinqueins-

                                             tance当返回值)。

让我们写一个测试类(main.java):

1 public class main {
2 
3     public static void main(string[] args) {
4         singleton singleton = singleton.getinstance();
5         system.out.println(singleton.getdescription());
6     }
7 }

结果展示:

 

单件模式:确保一个类只有一个实例,并提供一个全局访问点。

好啦,我们从上面的示例简单学习了单件模式;现在让我们来看一个更加复杂的示例

(巧克力工厂):

 1 public class chocolateboiler {
 2     private boolean empty;
 3     private boolean boiled;
 4   
 5     private chocolateboiler() {
 6         empty = true;    //刚开始锅炉是空的
 7         boiled = false;
 8     }
 9 
10     public void fill() {
11         if (isempty()) { //在锅炉内填入原料时,锅炉必须是空的。一旦填入原料,就把empty和boiled标志设置好
12             empty = false; 
13             boiled = false;
14             //在锅炉内填满巧克力和牛奶的混合物
15         }
16     }
17  
18     public void drain() {//锅炉排出时,必须是满的(不可以是空的)而且是煮沸过的。排出完毕后,吧empty设置为true
19         if (!isempty() && isboiled()) {
20             // 排出煮沸的巧克力和牛奶
21             empty = true;
22         }
23     }
24  
25     public void boil() { //煮混合物时,锅炉必须是满的,并且是没有煮过的。一旦煮沸后,就把boiled设为true
26         if (!isempty() && !isboiled()) {
27             // 将炉内物煮沸
28             boiled = true;
29         }
30     }
31   
32     public boolean isempty() {
33         return empty;
34     }
35  
36     public boolean isboiled() {
37         return boiled;
38     }
39 }

我们在有意识地防止不好的事情发生,但是如果同时存在两个chocolateboiler实例,

可能就会发生很糟糕的事情哦!

所以我们把这个类设计成单件:

 1 public class chocolateboiler {
 2     private boolean empty;
 3     private boolean boiled;
 4     private static chocolateboiler uniqueinstance;
 5   
 6     private chocolateboiler() {
 7         empty = true;
 8         boiled = false;
 9     }
10   
11     public static chocolateboiler getinstance() {
12         if (uniqueinstance == null) {
13             system.out.println("creating unique instance of chocolate boiler");
14             uniqueinstance = new chocolateboiler();
15         }
16         system.out.println("returning instance of chocolate boiler");
17         return uniqueinstance;
18     }
19 
20     public void fill() {
21         if (isempty()) {
22             empty = false;
23             boiled = false;
24             // fill the boiler with a milk/chocolate mixture
25         }
26     }
27  
28     public void drain() {
29         if (!isempty() && isboiled()) {
30             // drain the boiled milk and chocolate
31             empty = true;
32         }
33     }
34  
35     public void boil() {
36         if (!isempty() && !isboiled()) {
37             // bring the contents to a boil
38             boiled = true;
39         }
40     }
41   
42     public boolean isempty() {
43         return empty;
44     }
45  
46     public boolean isboiled() {
47         return boiled;
48     }
49 }

测试类(main.java):

 1 public class chocolatecontroller {
 2     public static void main(string args[]) {
 3         chocolateboiler boiler = chocolateboiler.getinstance();
 4         boiler.fill();
 5         boiler.boil();
 6         boiler.drain();
 7 
 8         // 将返回已存在的实例,也就是boiler
 9         chocolateboiler boiler2 = chocolateboiler.getinstance();
10     }
11 }

 

我们现在模仿了第一个项目,把它做成了单件,但是现在的这个类完美吗?不!当然

不完美,这不问题出现了:这个机器竟然允许在加热的时候继续加原料。

我们现在化身为jvm老看看问题出在哪里吧:

 

现在让我们开始解决问题,处理多线程(延迟同步,读完下面按段话就懂喽):

方法①:

 1 public class singleton {
 2     private static singleton uniqueinstance;
 3 
 4     private singleton() {}
 5 
 6     public static synchronized singleton getinstance() {
 7         if (uniqueinstance == null) {
 8             uniqueinstance = new singleton();
 9         }
10         return uniqueinstance;
11     }
12 
13     //其他代码
14 }

第六行:通过增加synchronized关键字到getinstance()方法中,我们迫使每个线程在

进入这个方法之前,要先等候别的线程离开该方法。也就是说,不会有两个线程可同时

进入这个方法。

 

但是,我们是否能改善多线程呢?

      方法②(使用“急切”创建实例,而不是延迟实例的方法):

 1 public class singleton {
 2     private static singleton uniqueinstance = new singleton();
 3 
 4     private singleton() {}
 5 
 6     public static synchronized singleton getinstance() {
 7         return uniqueinstance;
 8     }
 9 
10     //其他代码
11 }

        方法③(双重检查加锁,在getinstance()中减少使用同步):

 1 public class singleton {
 2     private volatile static singleton uniqueinstance; 
 3 
 4     private singleton() {}
 5 
 6     public static synchronized singleton getinstance() {
 7         if (uniqueinstance == null) {
 8             synchronized(singleton.class){
 9                if(uniqueinstance == null){
10                    uniqueinstance = new singleton();
11                }
12             }
13         }
14         return uniqueinstance;
15     }
16 
17     //其他代码
18 }

注:volatile关键词确保:当uniqueinstance变量被初始化成singleton实例时,多个

       县城正确的处理uniqueinstance变量。

我们现在来对比一下三个方法:

       方法①同步getinstance方法:

                  这是保证可行的最直接的做法,对于巧克力锅炉似乎没有性能的考虑,

                  以可以用这个方法

       方法②急切实例化:

                 我们一定需要用到一个巧克力锅炉,所以静态的初始化实力并不是不行的。

                 虽然对于采用标准模式的开发人员来说,此做法可能稍微陌生一点儿。但也

                 是可行的。

       方法③双重检查加锁:

                 由于没有性能上的考虑,所以这个方法似乎杀鸡用了牛刀。另外,采用这个方法还得确定使用的是java5以上的版本。

 

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网