Neil啃設計模式(0x01)單例模式

單例模式(Singleton Pattern)

「單例模式(Singleton Pattern)是一個比較簡單的模式,其定義以下:
Ensure a class has only one instance, and provide a global point of access to it.(確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。)」
Excerpt From: 秦小波 著. 「設計模式之禪(第 2 版)(華章原創精品).」 iBooks.

定義

UML 示例

能夠經過plantuml查看html

@startuml
class Singleton{
   + name
}
note top:Singleton has a 'name' property.此處的Singleton表明下邊代碼中的File類

class Client
note left: client get one singleton instance
Client --> Singleton
@enduml

因爲此markdown不能展現故貼圖
image.pngjava

代碼實現

import threading
import time


class Singleton(type):
    def __new__(cls, *args, **kwargs):
        return type.__new__(cls, *args, **kwargs)

    def __init__(self, *agrs, **kwargs):
        self.__instance = None
        super().__init__(*agrs, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            # time.sleep(2) 模擬線程切換,此時不能保證多線程單例實現
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance


class File(metaclass=Singleton):
    def __init__(self, name):
        self.name = name


def create_file_instance(name):
    f = File(name)
    print(f)


# if __name__ == "__main__":
#     thread1 = threading.Thread(target=create_file_instance, args=('abc', ))
#     thread2 = threading.Thread(target=create_file_instance, args=('bcd', ))

#     thread1.start()
#     thread2.start()
# 若是有sleep會出現此狀況:
#<__main__.File object at 0x1052d34a8>
#<__main__.File object at 0x1052d3390>
# 若是沒有高几率不會出現不一致的狀況,由於代碼消耗的cpu時間很短,不足以形成線程切換
# -------------------多線程下單例實現---------------- #


def synchronized(func):
    func.__lock__ = threading.Lock()

    def lock_func(*args, **kwargs):
        with func.__lock__:
            return func(*args, **kwargs)

    return lock_func


class SingletonSync(type):
    def __init__(self, *agrs, **kwargs):
        self.__instance = None
        super().__init__(*agrs, **kwargs)

    @synchronized
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            time.sleep(2)
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance


class FileSync(metaclass=SingletonSync):
    def __init__(self, name):
        self.name = name


def create_file_instance_sync(name):
    f = FileSync(name)
    print(f)


if __name__ == "__main__":
    thread1 = threading.Thread(target=create_file_instance_sync, args=('abc', ))
    thread2 = threading.Thread(target=create_file_instance_sync, args=('bcd', ))

    thread1.start()
    thread2.start()
#<__main__.FileSync object at 0x10fbd43c8>
#<__main__.FileSync object at 0x10fbd43c8>
參考代碼:Python Cookbook - 9.13.2 章節
同步鎖機制: https://blog.csdn.net/lucky40...

知識點

學習《設計模式之禪》這本書,但又沒有使用 java 語言,便突發奇想,結合設計模式的書,參考 python 的相關書籍編寫 python 的設計模式的例子。相對於 java 來講單例應該是設計模式中最好理解和最好編寫的代碼了,可是對於 python 而言,小白的我,仍是學習了不少知識點。python

  • metaclass ( __metaclass__ )
    metaclass 能夠指定某個類的元類,何爲元類?就是建立類的類。 - 存在乎義:攔截類的建立、修改類、返回修改以後的類。 - 回到咱們的單例模式,就是利用元類來實現的,Singleton 類就是 File 類的元類,能夠理解爲是 Singleton 建立了 File 類,(注意是類而不是類的實例)。具體就是調用了__new__方法。設計模式

    • __metaclass__是實現自定義元類的一種方式,很少解釋,有興趣的能夠參考以下兩邊文章,說的比較好,並且有 metaclass 更好的應用場景介紹markdown

      https://www.cnblogs.com/Simon... https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072
  • __new__ 方法:很是基礎的知識點,newe 函數是執行 init 以前執行的函數,真正建立類的是 new 函數,new 函數會逐級查找__metaclass__(元類方法),若是找到就執行,找不到就繼續查找父類,若是都沒有最終會調到 type,type 也是一個元類,並且是像 int、string 等的元類,就是 type 建立了 int、string
  • __call__ 方法:File()執行的時候被調度的函數。全部定義了 call 方法的類均可以被稱爲是可執行的,就是能夠當成一個函數執行。

說了這幾個知識點,若是不太懂元類,或者 call 方法,可能仍是不太好理解這個單例的執行,一個簡單地方式就是回去跑一下試試。
說一下代碼執行流程(只介紹 File 建立的流程):多線程

在 File()執行以前,在 File 被定義傳來的時候 Singleton 的 new 函數就被執行了,目的就是爲了建立出這個 File 類,這也是爲何會寫上 new 函數的緣由,其實不寫不會影響單例模式,而後 Singleton 的 init 被執行,目的是爲了建立 instance 實例,實例名稱也有講究,雙下劃線表示隱私屬性不會被複寫。接着,當 File 函數被執行時會調用 call 方法。ide

sleep 函數是爲了模擬線程間切換,若是不切換高几率是不會出現建立兩個不一樣的實例的狀況。函數

下集預告:工廠模式

說給本身聽,當是給本身學習的一個動力吧!學習

相關文章
相關標籤/搜索