一、問題的引出
目的:設計一個加密的工具類。
寫法一:【單例模式】
public class CipherUtils { private List key; private static CipherUtils instance; private CipherUtils(String... args){ // TODO 比較複雜的初始化KEY的過程 } public static boolean initInstance(String... args){ instance = new CipherUtils(args); return true; } public static CipherUtils getInstance(){ return instance; } // 正在用到的加密方法 public String doCipher(String... args){ // TODO Map map = new HashMap(); map.put("key", key); return null; } }
》》呼叫方法:
CipherUtils cu= CipherUtils.getInstance();
cu.doCipher(...);
寫法二:【靜態方法模式】
public class CipherUtils { private static List key; // 正在用到的加密方法 public static String doCipher(String... args){ // TODO Map map = new HashMap(); map.put("key", key); return null; } // 比較複雜的初始化KEY的過程 public static void initKey(String... args){ // TODO } }
》》呼叫方法:CipherUtils.doCipher(...);
注意以上例子,只是一個模擬,真實情況 類的屬性可能不止一個,可能有三個以上屬性。
寫法一的特點:
1、使用時調的方法都是非static的(doCipher等等)
2、類的屬性都是非static的。
3、擁有單例模式的所有優點。
寫法二的優點:
1、屬性和方法都是static的。
2、static是全域性共享的,所以也擁有單例模式的核心優點。
另外,還注意到一點,單例模式 有一個比 靜態方法模式 更好的地方:
它可以被繼承,方法可以被子類過載,所以擴充套件性更強。
例如一個子類SubCipher過載了doCipher方法,那麼呼叫方式如下:
CipherUtils cu= SubCipher.getInstance();
cu.doCipher(...);
注意到,呼叫處(第二行)是無需修改程式碼的。但是如果是靜態方法的話,只能重新命名一個方法了,而且呼叫出還要改程式碼,比如:
CipherUtils.doCipher_02(...);
二、問題的研究
這一部分主要參考了網上3、4篇關於類似問題的討論。
比如:
http://www.blogjava.net/ITdavid/archive/2008/01/22/176939.html
http://wenku.baidu.com/view/f08eb06125c52cc58bd6befe.html
具體分析見第三節-總結。
三、總結
1、搞清楚單例模式的缺點和優勢,不要亂用。
一般我不用單例模式,因為這樣增加了程式碼管理成本。但是以下幾個場景最好用單例模式:
1)場景一:類可能會有繼承、方法可能需要過載。(Spring裡面有些東西就是這樣設計的)
2)場景二:類的所有屬性需要保持一致性,(要麼都改變,要麼都不改變),且這個類的方法較多(5個以上)。單例模式實現這個功能就比較方便,直接把原來快取的static例項物件整體替換掉,重新new一個就可以了,
但是如果用靜態方法來實現,則在更新屬性時,需要停止所有的static方法被呼叫,也就意味著,要在所有的方法中加上synchronized關鍵字,即:單例模式只需要同步getInstance這個方法就可以,而靜態方法模式,需要同步所有的方法。
如果類的例項方法不多,在5個以內,那其實給每個方法都加上synchronized關鍵字那也無所謂,反正效率上講,靜態方法還稍微高一些,但是如果方法多了,則還是採用單例模式吧。
2、就我上面,那個問題而言,最好是採用靜態方法模式,因為只有一個doCipher方法,不符合場景二,而且本身是個工具類,無需類的繼承,所以也不符合場景一。
PS:參考Struts的原始碼,我們其實可以對同步靜態方法進行適當改造。單例模式有一個缺點是,無論你做什麼操作,都需要同步,效能上稍微低了一點點,
靜態方法則可以採用一種“讀寫鎖”的模式,當有“寫操作”時,則不允許其他操作,當只有“讀操作時”,則不允許“寫操作”但是允許其他讀操作。其實現可以參考struts的原始碼:
com.opensymphony.xwork2.util.logging.LoggerFactory;
java.util.concurrent.locks.ReadWriteLock;
java.util.concurrent.locks.ReentrantReadWriteLock;