2010/02/09

Singleton

Singleton恐怕是受到最多批評的Design Pattern吧。被開發者批評的程度大概可以跟因太過複雜而被貶低的Vistor Pattern一較長短。

但是Singleton的想法基礎非常簡單,應用程式的執行中,就是「保證特定型的實例只能有一個存在」。

幾乎所有語言都能夠創造出無數個實例,直到J2SE 5以前,Java並沒有直接可以限制實例數的方法。J2SE 5以後的版本,
使用enum構造就可以限制特定型的物件的數量。以及也可以將已經知道的實例各自的取上名字。

C和C++等的語言,enum(列舉)是一個簡單的連串整數,每個都對應到一個Symbol。藉由使用enum,可以做出更有表現力的程式碼。

在Java裡的enum一見之下跟其他語言一樣,但是有幾個重要的不同點,enum本身可以持有建構子、方法、甚至是欄位等,但是卻沒辦法Subclass化。

以enum的定義來看,已知實例的名字已經在最初就指定好了,所以其他的實例不管用什麼方法都沒辦法作成。

單純的來想,Singleton Pattern的範例大概就會想到圖書館系統裡的目錄。無法瞭解每本書被加入到哪個目錄是我們不希望的狀況,
在下面的List1是使用了enum構造,創造出Singleton的目錄並實裝的範例。

import java.util.*;

public enum Catalog {
   soleInstance;

   private Map<String,Book> books = new HashMap<String,Book>();

   public void add(Book book) {
      books.put(book.getClassification(), book);
   }

   public Book get(String classification) {
      return books.get(classification);
   }
}

並不是這麼困難的事情吧~看完上面後~Client code就知道了一定得使用已知的單一物件soleInstance了~
下面是List2(Client Code)

import static org.junit.Assert.*;
import org.junit.*;

public class CatalogTest {
   @Test
   public void singleBook() {
      Book book = new Book("QA123", "Schmidt", "Bugs", "2005");
      Catalog.soleInstance.add(book);
      assertSame(book, Catalog.soleInstance.get("QA123"));
   }
}

如果想要直接實例化Catalog物件的話會無法編譯

new Catalog(); // this won't compile!

大部分的開法者大概比較熟悉自己完結型的Singleton的概念吧。在Java裡是使用static方法,並將建構子設為private的意思。

下面是自我完結型的例子(List3)

import java.util.*;

public class Catalog {
   private static final Catalog soleInstance = new Catalog();
   private Map<String,Book> books = new HashMap<String,Book>();

   public static Catalog soleInstance() {
      return soleInstance;
   }

   private Catalog() {
      // enforce singularity
   }

   public void add(Book book) {
      books.put(book.getClassification(), book);
   }

   public Book get(String classification) {
      return books.get(classification);
   }
}

藉著private的建構子,就可以保證其他的client無法作成Catalog物件,從Client的觀點來看的話,並看不出什麼太大的差別。(參考List4)

import static org.junit.Assert.*;
import org.junit.*;

public class CatalogTest {
   @Test
   public void singleBook() {
      Book book = new Book("QA123", "Schmidt", "Bugs", "2005");
      Catalog.soleInstance().add(book);
      assertSame(book, Catalog.soleInstance().get("QA123"));
   }
}
這樣看來好像是一個單純無害的Pattern,不過到底是什麼原因讓它受到如此多的批評呢?

Singleton只不過是將全域變數換成物件導向的表示法而已!在軟體開發裡,很早以前全域變數引起的問題就被發現了。
最大的問題是,全域變數是跟整個應用程式是緊密結合的。

首先要先檢討的是,全域變數跟Singleton的使用是真的必要的嘛?在現實上,主目錄只會有一個,但是設計一個Catalog類裡books的HashMap定義為static封裝是可能的。
在這種時候,Client會根據必要而複數作成Catalog實例,但是全部都指向相同的static欄位(順帶一提,這種Pattern叫做Monostate)

對於全域變數和Singleton的否定聲音非常多,但是毫無疑問這是一個正當的構造。

http://japan.internet.com/developer/20080826/26.html

No comments:

Post a Comment