javascript|設計
即使是簡單的指令碼語言,應用良好的模式可以得到非常“優美”的代碼和較高的效率。
尤其是對於互動要求較高的B/S系統,非常有必要用設計模式來最佳化代碼。
單件模式(Singleton Pattern)是一種非常基本和重要的建立型模式。
“單件”的職責是保證一個類有且只有一個執行個體,並提供一個訪問它的全域訪問點。
在程式設計過程中,有很多情況下需要確保一個類只能有一個執行個體。
傳統的程式設計語言中為了使一個類只有一個執行個體,最容易的方法是在類中嵌入靜態變數,並在第一個執行個體中設定該變數,而且每次進入建構函式都要做檢查,不管類有多少個執行個體,靜態變數只能有一個執行個體。為了防止類被多次初始化,要把建構函式聲明為私人的,這樣只能在靜態方法裡建立一個執行個體。
在javascript中,雖然我們仍然可以指定靜態方法來構造對象,但由於我們不能利用建構函式的“私人”來禁止多個執行個體的產生,因此要完全實現Singleton並沒有想象中那麼簡單。
請看下面的例子:
以下內容為程式碼
<script>
function SingletonObject()
{
SingletonObject.prototype.methodA = function()
{
alert('methodA');
}
SingletonObject.prototype.methodB = function()
{
alert('methodB');
}
SingletonObject.instance = this;
}
SingletonFactory = new Object();
SingletonFactory.getInstance = function()
{
if(SingletonObject.instance == null)
return new SingletonObject();
else
return SingletonObject.instance;
}
var instA = SingletonFactory.getInstance();
var instB = SingletonFactory.getInstance();
instA.methodA();
instB.methodA();
alert(instA == instB); //成功
var instC = new SingletonObject();
instC.methodA();
alert(instA == instC); //失敗
</script>
上面的例子試圖通過傳統的方式來實現Singleton模式,而通過調用SingletonTest.getInstance()來獲得對象確實可以保證“唯一執行個體”,然而,這個例子的失敗之處在於它並沒有有效地禁止Singleton對象的構造,因此如果我們在程式碼中人工加入new SingletonObject(),仍然可以獲得到多個對象而導致模式失敗。
一個改進的替代方案如下:
以下內容為程式碼
<script>
function SingletonObject()
{
if(SingletonObject.instance != null)
{
alert("不能建立多個singleton執行個體!");
throw new Error();
}
SingletonObject.prototype.methodA = function()
{
alert('methodA');
}
SingletonObject.prototype.methodB = function()
{
alert('methodB');
}
SingletonObject.instance = this;
}
SingletonFactory = new Object();
SingletonFactory.getInstance = function()
{
if(SingletonObject.instance == null)
return new SingletonObject();
else
return SingletonObject.instance;
}
var instA = SingletonFactory.getInstance();
var instB = SingletonFactory.getInstance();
instA.methodA();
instB.methodA();
alert(instA == instB); //成功
try
{var instC = new SingletonObject(); }//拋出異常
catch(e)
{alert('系統成功拋出了異常,阻止了instC的構造!');}
</script>
這樣當使用者試圖自己建立多個對象的時候,通過人工拋出異常來阻止。不過這麼做還是有一點點違反了"初衷",即沒有滿足“必須通過靜態方法來構造唯一執行個體”這個基本條件。因為使用者可以在最開始的時候還是可以採用new操作符來構造對象,比如在一開始寫var instA = new SingletonObject()來構造instA並不會導致拋出異常,這不能不說是這種方法的一個缺陷。
於是我們進一步思考,得到了下面第三種方法,這種方法巧妙利用了“匿名”函數的特徵來禁止對SingletonObject類建構函式的訪問,可以說比較好的類比了私人建構函式的特性,從而比較完美地解決了用javascript實現Singleton Pattern的問題。
以下內容為程式碼
<script>
(function(){
//instance declared
//SingletonFactory Interface
SingletonFactory = {
getInstance : getInstance
}
//private classes
function SingletonObject()
{
SingletonObject.prototype.methodA = function()
{
alert('methodA');
}
SingletonObject.prototype.methodB = function()
{
alert('methodB');
}
SingletonObject.instance = this;
}
//SingletonFactory implementions
function getInstance()
{
if(SingletonObject.instance == null)
return new SingletonObject();
else
return SingletonObject.instance;
}
})();
var instA = null;
try
{
alert("試圖通過new SingletonObject()構造執行個體!");
instA = new SingletonObject();
}
catch(e){alert("SingletonObject建構函式不能從外部存取,系統拋出了異常!");}
instA = SingletonFactory.getInstance(); //通過Factory上定義的靜態方法獲得
var instB = SingletonFactory.getInstance();
instA.methodA();
instB.methodA();
alert(instA == instB); //成功
var instC = null;
try
{
alert("試圖通過new SingletonObject()構造執行個體!");
instC = new SingletonObject();
}
catch(e){alert("SingletonObject建構函式不能從外部存取,系統拋出了異常!");}
</script>