設計模式--單件模式Singleton(建立型)

benbenxiongyuan發表於2014-04-10

轉自: http://blog.csdn.net/hguisu/article/details/7515416

幾乎所有物件導向的程式中,總有一些類的物件需要是唯一的,例如,通過資料庫控制程式碼到資料庫的連線是獨佔的。您希望在應用程式中共享資料庫控制程式碼,因為在保持連線開啟或關閉時,它是一種開銷。再如大家最經常用的IM,如QQ,在同一臺電腦,一個帳號只能有唯一的登入。

1. 問題

怎樣確保一個特殊類的例項是獨一無二的(它是這個類的唯一例項),並且這個例項易於被訪問呢?


2. 解決方案

1)全域性變數:一個全域性變數使得一個物件可以被訪問,但它不能防止你例項化多個物件。因為你的任何程式碼都能修改全域性變數,這將不可避免的引起更多除錯的意外。換句話說,全域性變數的狀態總是會出現一些問題的。

2)類建構函式私有和類自身的靜態方法:讓類自身負責儲存它的唯一例項(靜態變數)。這個類可以保證沒有其他例項可以被建立(通過擷取建立新物件的請求) ,並且它可以提供一個訪問該例項的方法(靜態方法)。這就是Singleton模式。


3. 適用性
在下面的情況下可以使用單件模式
1)當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時。

2)當這個唯一例項應該是通過子類化可擴充套件的,並且客戶應該無需更改程式碼就能使用一個擴充套件的例項時。

4. 實現:

UML結構:


程式碼:

 

<?php
class  Singleton {
	static  private  $_instance = null;//靜態成員儲存唯一例項
	/**
	 * 私有建構函式,保證不能被外部訪問
	 *
	 */
	private function __construct() {} 
	/**
	 * 靜態方法將建立這個例項的操作並保證只有一個例項被建立
	 *
	 * @return unknown
	 */
	public static function getInstance() {
		if (!self::$_instance) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
}

5. 效果

   Singleton模式有許多優點
1)  對唯一例項的受控訪問, 因為Singleton類封裝它的唯一例項,所以它可以嚴格的控制客戶怎樣以及何時訪問它。
2)  縮小名空間,Singleton模式是對全域性變數的一種改進。它避免了那些儲存唯一例項的全域性變數汙染名空間。
3)  允許對操作和表示的精化Singleton類可以有子類,而且用這個擴充套件類的例項來配置一個應用是很容易的。你可以用你所需要的類的例項在執行時刻配置應用。
4)  允許可變數目的例項 這個模式使得你易於改變你的想法,並允許Singleton類的多個例項。此外,你可以用相同的方法來控 制應用所使用的例項的數目。只有允許訪問 Singleton例項的操作需要改變。


6 .單件模式可以多個例項

     單件模式並不是說一個類只能只有一個例項。假設我們使用在一個web 請求或者程式裡面。一個使用者id對應的某個類只能有唯一的例項。在下面的例子中,我們的User類,可以有多個例項,每個例項對應一個uid. 例項列表註冊到靜態變數$_instance並和uid關聯起來。最簡單的例子是我們前面提到的QQ,在同一臺電腦,可以使用多帳號登入, 但一個帳號只能有唯一的登入.

    程式碼:

<?php
class  User {
	static  private  $_instance = array();//靜態成員儲存唯一例項
	private $_uid ;
	/**
	 * 私有建構函式,保證不能被外部訪問
	 *
	 */
	private function __construct($uid ) {
		$this->_uid = $uid;
	} 
	/**
	 * 靜態方法將建立這個例項的操作並保證只有一個例項被建立
	 *
	 * @return unknown
	 */
	public static function getInstance($uid = 0) {
		if (!self::$_instance || !isset(self::$_instance[$uid]) ) {
			self::$_instance[$uid] = new self($uid);
		}
		return self::$_instance[$uid];
	}
}

 

IOS中的單例模式


Apple官方建議  

由於自己設計單態模式存在一定風險,主要是考慮到可能在多執行緒情況下會出現的問題,因此蘋果官方建議使用以下方式來實現單態模式:

在objective-c中要實現一個單例類,至少需要做以下四個步驟:
  1、為單例物件實現一個靜態例項,並初始化,然後設定成nil,
  2、實現一個例項構造方法檢查上面宣告的靜態例項是否為nil,如果是則新建並返回一個本類的例項,
  3、重寫allocWithZone方法,用來保證其他人直接使用alloc和init試圖獲得一個新實力的時候不產生一個新例項,
  4、適當實現allocWitheZone,copyWithZone,release和autorelease。

 

ARC模式下使用單例,首先建立一個SingletonExample類,下面是SingletonExample.h檔案中的:

#import
@interface SingletonExample : NSObject

+(SingletonExample *)sharedInstance;
@end

 

然後就是SingletonExample.m中的#import “SingletonExample.h”
@interface SingletonExample ()
-(id)initialize;
@end

 

 


@implementation SingletonExample
+(SingletonExample *)sharedInstance
{
  static SingletonExample *sharedSingleton = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken,^(void) {
    sharedSingleton = [[self alloc] initialize];
  });
  return sharedSingleton;
}


-(id)initialize 
{
  if(self == [super init] )
  {
    //initial something here
   }
 return self;
}
@end

 

如果程式碼的目標想相容iOS 4以下的系統,那麼上述程式碼中的sharedInstance方法裡面的GCD便不能用了,可以用下面的方法進行替代+(SingletonExample *)sharedInstance
{
  static SingletonExample *sharedSingleton = nil; //第一步:靜態例項,並初始化。 
  @synchronized([SingletonExample class]) { //第二步:例項構造檢查靜態例項是否為nil
     if(sharedSingleton == nil)
     {
        sharedSingleton = [[self alloc] initialize];
      }
   }
 return sharedSingleton;
}

這樣的話就能相容所有的iOS裝置了,不過@synchronize貌似不太好用,效率相比GCD比較低。
dispatch_once_t或者是@synchronized方法,保證執行緒安全。

上面這些方法呢,都是在ARC下面使用的,如果使用的非ARC,其實也很簡單,在SingletonExample.m中加入下面這些即可:

 

-(id)allocWithZone:(NSZone *)zone {//第三步:重寫allocWithZone方法   

   @synchronized (self) {       

       if (sharedSingleton== nil) {           

              sharedSingleton= [super allocWithZone:zone];           

              return sharedSingleton;       

       }   

   }   

return nil;

}

 

-(id)copyWithZone:(NSZone *)zone {//第四步:適當實現   

   return self;

}

 

- (id)retain {  

    return self;

}

 

- (unsigned)retainCount {  

    return UINT_MAX; //denotes an object that cannot be released

}

 

- (oneway void)release {   

    // never release

}

 

- (id)autorelease {  

   return self;

}

 

- (void)dealloc {  

   // Should never be called, but just here for clarity really.

   //release what you initialize  

   [super dealloc];

}

 

然後再把sharedInstance方法給改一下

static SingletonExample *sharedSingleton = nil;

+(SingletonExample *)sharedInstance {  

     @synchronized(self) {      

          if(sharedSingleton == nil)     

          sharedMyManager = [[super allocWithZone:NULL] init]; 

     } 

     return sharedSingleton;

}

相關文章