一、單例模式存在的意義 在這裡的單例就是只有一個實例(這裡的實例就像在面向對象的時候,創建了一個對象也可以說創建了一個實例),只用一個實例進行程式設計,首先我們可以瞭解一下什麼時候不適合使用單例模式,比如我們需要使用類同時創建多個對象的時候,且每個對象中封裝了不同的數據的時候,就不能使用單例模式,就 ...
一、單例模式存在的意義
在這裡的單例就是只有一個實例(這裡的實例就像在面向對象的時候,創建了一個對象也可以說創建了一個實例),只用一個實例進行程式設計,首先我們可以瞭解一下什麼時候不適合使用單例模式,比如我們需要使用類同時創建多個對象的時候,且每個對象中封裝了不同的數據的時候,就不能使用單例模式,就像下麵的例子
class Person: def __init__(self,name,age): self.name = name self.age = age mingming = Person('明明',19) mingyue = Person('明月',30) mingri = Person('明日',20)
那麼什麼時候適合使用單例模式,如果每個對象中封裝了相同的數據,但是需要創建多個對象的時候,而且這兩個實例所有的功能是一樣的,所以我們就可以使用一個實例完成,在這裡我們就可以使用單例模式,如下
class Person: def __init__(self): self.name = '123123' self.age = '20' def f1(self): pass def f2(self): pass mingming = Person() mingming.f1() mingyue = Person() mingyue.f1()
為什麼要使用單例模式,就是為了在封裝數據相同的情況下,而且每個實例可執行的方法相同的時候,不必創建多個對象,只需要使用一個實例,來完成多個相同實例所完成的功能,通過這種方式減少記憶體的使用。
二、單例模式應用的場景
- 需要頻繁的進行創建和銷毀的對象;
- 創建對象時耗時過多或耗費資源過多,但又經常用到的對象;
- 工具類對象;
- 頻繁訪問資料庫或文件的對象。
三、單例模式的優點以及缺點
優點
系統記憶體中該類只存在一個對象,節省了系統資源,對於一些需要頻繁創建銷毀的對象,使用單例模式可以提高系統性能。
由於單例模式在記憶體中只有一個實例,減少了記憶體開銷。
單例模式可以避免對資源的多重占用,例如一個寫文件時,由於只有一個實例存在記憶體中,避免對同一個資源文件的同時寫操作。
單例模式可以再系統設置全局的訪問點,優化和共用資源訪問。
其中使用到單例模式時,考慮較多的就是多線程的情況下如何防止被多線程同時創建等問題。
當這個類的對象在多個地方創建的時候,使得內部的方法多次調用,但是希望只要一個對象操作這個方法,或者不希望多個地方同時調用這個方法,需要保持這個方法的單一性質,就用單利模式
缺點
使用單例模式,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現。
四、單例模式代碼編寫
接下來我們就模擬一個資料庫連接池來實現單例模式
首先我們介紹一下資料庫連接池
我們通過程式進行資料庫的操作的時候,每次都需要連接資料庫,但是連接資料庫需要消耗較多的時間,所以我們可以在我們主機的記憶體里維護一個資料庫連接池,在這個連接池中有若幹個已經連接資料庫的連接,我們想要連接資料庫的時候直接到連接池中取出一個連接即可,省去了連接的時間。
非單例模式
import random class SqlConnectionPool: __instance = None def __init__(self): self.ip = "127.0.0.1" self.port = 3306 self.pwd = '123456' self.username = 'alexsel' #去連接 self.conn_list = [1,2,3,4,5,6,7,8,9,10] def get_connection(self): #獲取連接 r = random.randrange(1,11) return r #我們創建多個對象記憶體地址是一樣的,說明拿到的是同一個對象 obj = SqlConnectionPool() print(obj) obj1 = SqlConnectionPool() print(obj1) obj2 = SqlConnectionPool() print(obj2) 輸出結果: <__main__.SqlConnectionPool instance at 0x0000000002630788> <__main__.SqlConnectionPool instance at 0x0000000002630888> <__main__.SqlConnectionPool instance at 0x00000000026308C8>
非單例模式每次記憶體輸出的結果都不同,下麵是單例模式。
import random class SqlConnectionPool: __instance = None def __init__(self): self.ip = "127.0.0.1" self.port = 3306 self.pwd = '123456' self.username = 'alexsel' #去連接 self.conn_list = [1,2,3,4,5,6,7,8,9,10] @staticmethod #靜態方法是由類調用的 def get_instance(): if SqlConnectionPool.__instance: return SqlConnectionPool.__instance else: #創建一個對象,並將對象賦值給靜態欄位__instance SqlConnectionPool.__instance = SqlConnectionPool() return SqlConnectionPool.__instance #單例模式關鍵代碼解析 #當第一次調用這個靜態方法的時候,判斷__instacne的值是None所以執行else,然後在else中創建一個對象賦給靜態欄位__instance,然後返回這個靜態欄位 #第二次調用這個今靜態方法的時候,判斷__instance的值為真,所以直接返回這個靜態欄位,而這個靜態欄位中包含的是第一次創建的對象,所以在以後調用這個方法 #就只會調用第一次創建的對象,這就是單例模式 def get_connection(self): #獲取連接 r = random.randrange(1,11) return r #我們創建多個對象記憶體地址是一樣的,說明拿到的是同一個對象 obj = SqlConnectionPool.get_instance() print(obj) obj1 = SqlConnectionPool.get_instance() print(obj1) obj2 = SqlConnectionPool.get_instance() print(obj2) 輸出結果: <__main__.SqlConnectionPool instance at 0x000000000260C808> <__main__.SqlConnectionPool instance at 0x000000000260C808> <__main__.SqlConnectionPool instance at 0x000000000260C808>
以上是基於類,使用靜態欄位和靜態方法實現的一個單例模式。