單例模式實例一
作爲一個實際的用例,我們將通過一個數據庫應用程序來展示單例的應用。這裏不妨以需要對數據庫進行多種讀取和寫入操作的雲服務爲例進行講解。完整的雲服務被分解爲多個服務,每個服務執行不同的數據庫操作。
很明顯,跨不同的服務的共享資源是數據庫本身。因此,如果我們需要要更好地設計雲服務,必須注意以下幾點。
① 數據庫中操作的一致性,即一個操作不應該與其他操作發生衝突
② 優化數據庫的各種操作,以提高內存和CPU的利用率
示例:
import sqlite3
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=MetaSingleton):
connection = None
def connect(self):
if self.connection is None:
self.connection = sqlite3.connect("db.sqlite3")
self.cursor_obj = self.connection.cursor()
return self.cursor_obj
db1 = Database().connect()
db2 = Database().connect()
print("Database Objects DB1", db1)
print("Database Objects DB2", db2)
通過上面的代碼,我們會發現一下幾點:
1、我們以MetaSingleton爲名創建了一個元類,就像之前說的那樣,Python的特殊方法__call__可以通過元類創建單例
2、數據庫類由MetaSingleton類裝飾後,其行爲就會表現爲單例。因此,當數據庫被實例化時,它只創建一個對象
3.當Web應用程序對數據來執行某些操作時,它會多次實例化數據庫類,但只創建一個對象。因爲只有一個對象,所以數據庫調用是同步的。此外,這樣還能夠節約系統資源,並且可以避免消耗過多的內存或CPU資源
假如我們要開發的不是單個web應用程序,而是集成化的形式,即多個web應用共享單個數據庫。單例在這種情況下不太好使,因爲每增加一個web程序,就要新建一個單例,添加一個新的對象來查詢數據庫。這導致數據庫操作無法同步,並且要耗費大量的資源。在這種情況下,數據庫連接池比實現單例要好的多。
單例模式實例二
當我們爲基礎設施提供運行狀況監控服務(就像Nagios監控工具一樣)。我們創建了HealthCheck類,它作爲單例實現。我們還要維護一個被監控的服務器列表。但一個服務器從這個列表中刪除時,監控軟件應該察覺到這一情況,並從被監控的服務器列表中將其刪除。
class HealthCheck(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not HealthCheck._instance:
HealthCheck._instance = super(HealthCheck, cls).__new__(cls, *args, **kwargs)
return HealthCheck._instance
def __init__(self):
self.servers = []
def add_server(self):
self.servers.append("Server 1")
self.servers.append("Server 2")
self.servers.append("Server 3")
self.servers.append("Server 4")
def change_server(self):
self.servers.pop()
self.servers.append("Server 5")
hc1 = HealthCheck()
hc2 = HealthCheck()
print(hc1, hc2)
hc1.add_server()
print("Schedule health check for servers (1)..")
for i in range(4):
print("Checking ", hc1.servers[i])
hc2.change_server()
print("Schedule health check for servers (2)..")
for i in range(4):
print("Checking ", hc2.servers[i])
在上面的代碼中,hc1和hc2對象與單例中的類相同。
我們可以使用add_server()方法將服務器添加到基礎設施中,以進行狀況檢查。首先,通過迭代對這些服務器的運行狀況進行檢查。之後,changeServer()方法會刪除最後一個服務器,並計劃進行運行狀況檢查的基礎設施中添加一個新的服務器。因此,當運行狀況檢查進行第二次迭代時,他會使用修改後的服務器列表。
所有的這一切都可以藉助單例模式來完成。當添加或刪除服務器時,運行狀況的檢查工作必須由瞭解基礎設施變動情況的同一個對象來完成。
單例模式的缺點
由於單例具有全局訪問權限,因此可能會出現以下問題:
1、全局變量可能在某處已經被誤改,但是開發人員仍然認爲它們沒有發生變化,而該變量還在應用程序的其他位置被使用
2、可能會對同一對象創建多個引用。由於單例只創建一個對象,因此這種情況下會對同一個對象創建多個引用
3、所有依賴於全局變量的類都會由於一個類的改變而緊密耦合爲u全局數據,從而可能無意中影響另一個類
總結:
1、在許多實際應用程序中,我們只需要創建一個對象,如線程池、緩存、對話框、註冊表設置等。如果我們爲每個應用程序創建多個實例,則會導致資源過度使用。單例模式在這種情況下工作的很好
2、單例是一種經過時間考驗的成熟方法,能搞在不帶來太多缺陷的情況下提供全局訪問點
3、當使用全局變量或類的實例化非常耗費資源但最終卻沒有用到它們的情況下,單例的影響可以忽略不計