天天看點

java 設計模式 學習筆記(五)單例模式

單例模式:用來建立獨一無二的,隻能有一個執行個體的對象的入場券。

   一些對象,我們隻需要一個:(線程池,緩存,對話框等等),事實上,這類對象隻能有一個執行個體。如果制造多了了,會導緻許多問題,如行為異常、資源使用過量。

   全局變量的缺點,如果将對象指派給一個全局變量,那麼必須在程式一開始就建立好對象,萬一這個對象非常消耗資源,而程式在這次的執行過程中又一直沒用到它,就形成了浪費。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>public</code> <code>class</code> <code>Singleton {</code>

<code>    </code><code>private</code> <code>static</code> <code>Singleton uniInstance;</code>

<code>    </code><code>public</code> <code>int</code> <code>ID;</code>

<code>                                                                                                                                                                                                                                                                                                                                   </code> 

<code>    </code><code>private</code> <code>Singleton(</code><code>int</code> <code>id) {</code>

<code>        </code><code>ID = id;</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>static</code> <code>Singleton getInstance(</code><code>int</code> <code>id) {</code>

<code>        </code><code>if</code> <code>(uniInstance == </code><code>null</code><code>) {</code>

<code>            </code><code>uniInstance = </code><code>new</code> <code>Singleton(id);</code>

<code>        </code><code>}</code>

<code>        </code><code>return</code> <code>uniInstance;</code>

<code>}</code>

<code>                                                                                                                                                                                                                                                                                                                                  </code> 

<code>//----------------------Main------------------------</code>

<code>public</code> <code>class</code> <code>Main {</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>

<code>        </code><code>Singleton mSingleton,nSingleton;</code>

<code>        </code><code>mSingleton = Singleton.getInstance(</code><code>3</code><code>);</code>

<code>        </code><code>nSingleton = Singleton.getInstance(</code><code>4</code><code>);</code>

<code>        </code><code>System.out.println(mSingleton.ID);</code>

<code>        </code><code>System.err.println(nSingleton.ID);</code>

   列印出來的結果,都是3,說明mSingleton與nSingleton指向同一對象,保證了執行個體的唯一性。

請注意,如果不需要這個執行個體,它就永遠不會産生。

   上述為單例模式的經典實作,再看下單例模式的定義:

   確定一個類隻有一個執行個體,并提供一個全局通路點。

   把類設計成自己管理的一個單獨執行個體,同時也避免其他類再自行産生執行個體。并且提供了這個執行個體的全局通路點:當你需要執行個體時,向類查詢,它會傳回單個執行個體。

   需要注意的是,Singleton聲明的執行個體對象必須是全局的。假設上述代碼中的

<code>Singleton mSingleton,nSingleton;</code>

是分别放在不同線程中聲明的,那麼getInstance獲得的執行個體就不符合單件模式規則了。

   巧克力工廠:

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

<code>/**</code>

<code> </code><code>* 這是一個巧克力鍋爐控制器,鍋爐做的事,就是把巧克力和牛奶融在一起, 然後送到下一個階段,以制造成巧克力棒</code>

<code> </code><code>*/</code>

<code>                                                                  </code> 

<code>public</code> <code>class</code> <code>ChocolateBoiler {</code>

<code>    </code><code>private</code> <code>boolean</code> <code>empty;</code><code>// 代碼開始時,鍋爐是空的</code>

<code>    </code><code>private</code> <code>boolean</code> <code>boiled;</code>

<code>    </code><code>public</code> <code>ChocolateBoiler() {</code>

<code>        </code><code>empty = </code><code>true</code><code>;</code>

<code>        </code><code>boiled = </code><code>false</code><code>;</code>

<code>    </code><code>/**</code>

<code>     </code><code>* 在鍋爐内填入原料時,鍋爐必須是空的。 一旦填入原料,就把empty和boiled标志設定好</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>void</code> <code>fill() {</code>

<code>        </code><code>if</code> <code>(isEmpty()) {</code>

<code>            </code><code>empty = </code><code>false</code><code>;</code>

<code>            </code><code>boiled = </code><code>true</code><code>;</code>

<code>            </code><code>// 在鍋爐内填滿巧克力和牛奶的混合物</code>

<code>    </code><code>private</code> <code>boolean</code> <code>isEmpty() {</code>

<code>        </code><code>return</code> <code>empty;</code>

<code>                                                                </code> 

<code>    </code><code>private</code> <code>boolean</code> <code>isBoiled() {</code>

<code>        </code><code>return</code> <code>boiled;</code>

<code>     </code><code>* 鍋爐排出時,必須是滿的(不可以是空的)而且是煮過的。排出完畢後,把empty标志設回true</code>

<code>    </code><code>public</code> <code>void</code> <code>drain() {</code>

<code>        </code><code>if</code> <code>(!isEmpty() &amp;&amp; isBoiled()) {</code>

<code>            </code><code>// 排除煮沸的巧克力和牛奶</code>

<code>            </code><code>empty = </code><code>true</code><code>;</code>

<code>     </code><code>* 煮混合物時,鍋爐必須是滿的,并且是沒有煮過的,一旦煮沸後,就把boiled标志設為true</code>

<code>    </code><code>public</code> <code>void</code> <code>boil() {</code>

<code>        </code><code>if</code> <code>(!isEmpty() &amp;&amp; !isBoiled()) {</code>

<code>            </code><code>// 将爐内物煮沸</code>

   注意到了,如果同時存在兩個以上ChocolateBoiler執行個體存在,可能會存在的問題:排除500升的為主費的混合物,或者鍋爐已經滿了還繼續放原料,或者鍋爐内還沒放原料就開始空燒。修改下代碼:

<code>    </code><code>private</code> <code>static</code> <code>ChocolateBoiler uniChocolateBoiler;</code>

<code>                                       </code> 

<code>    </code><code>private</code> <code>ChocolateBoiler() {</code>

<code>        </code><code>// TODO Auto-generated constructor stub</code>

<code>    </code><code>public</code> <code>static</code> <code>ChocolateBoiler getInstance() {</code>

<code>        </code><code>if</code> <code>(uniChocolateBoiler == </code><code>null</code><code>) {</code>

<code>            </code><code>uniChocolateBoiler = </code><code>new</code> <code>ChocolateBoiler();</code>

<code>        </code><code>return</code> <code>uniChocolateBoiler;</code>

<code>    </code><code>//後面代碼省略</code>

   又遇到了麻煩,fill方法允許在加熱的過程中繼續假如原料,這可是會溢出五百升的原料。是多線程導緻的問題。    

   處理多線程,隻要把getInstance()變成同步(synchronized)方法:

<code>class</code> <code>Singleton {</code>

<code>    </code><code>private</code> <code>static</code> <code>Singleton uniqueInstance;</code>

<code>                              </code> 

<code>    </code><code>private</code> <code>Singleton() {</code>

<code>    </code><code>public</code> <code>static</code> <code>synchronized</code> <code>Singleton getInstance() {</code>

<code>        </code><code>if</code> <code>(uniqueInstance == </code><code>null</code><code>) {</code>

<code>            </code><code>uniqueInstance = </code><code>new</code> <code>Singleton();</code>

<code>        </code><code>return</code> <code>uniqueInstance;</code>

綜述

   1.單件模式確定程式中一個類最多隻有一個執行個體

   2.單件模式也提供通路這個執行個體的全局點

   3.在java中實作單件模式需要

   (1)私有構造器

   (2)一個靜态方法

   (3)一個靜态變量

   4.确定在性能和資源上的限制,然後小心地選擇适當的方案來實作單件,以解決多線程的問題

由于最近一直在學習Golang,是以之後學習的設計模式如果沒有涉及到Java特有的語言特性,學習筆記中的源碼将由Golang來完成

本文轉自 ponpon_ 51CTO部落格,原文連結:http://blog.51cto.com/liuxp0827/1353917,如需轉載請自行聯系原作者