程式設計之 同步靜態方法和單例模式的選擇

Zollty發表於2013-08-28

 

一、問題的引出

目的:設計一個加密的工具類。

寫法一:【單例模式】

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;

 

 

相關文章