天天看點

Python | 常見設計模式

作者:VT漫步

先想象一下這樣一個場景:去麥當勞點餐,我們想吃麥辣雞腿堡、薯條和可樂。我們當然可以單點,但是如果我們對收銀小姐姐說,請給我一份麥辣雞腿堡套餐,就會友善很多。套餐是餐廳和我們約定好了的一個規範,如果大家都按照這個規範來點餐,就能夠提高點餐效率。

建立套餐是提高點餐效率的可重複使用的解決方法。套餐可以重複被點,而且提高了顧客和服務員之間的共同效率。

類似地,在編寫程式的軟體世界,對于統一情境,衆多軟體開發任意經過長時間總結出了最佳的可重用的解決方案,就是軟體的設計模式。

軟體世界本沒有設計模式,開發人員多了,開發得久了,也便總結出了設計模式。

設計模式有很多很多種,對于不同的場景有不同的設計模式。這裡隻介紹兩種非常常見的設計模式:單例模式和工廠模式。

單例模式

我們從前學習的類建立對象時,每個對象都是開辟互相獨立的記憶體空間,它們各自的執行個體屬性和方法是互無聯系互不影響的。但是有的時候,我們隻想建立一個對象,不管被執行個體化多少次,使用的對象都是一個。

比如在開發過程中,不同的程式員負責不同的子產品。他們都需要使用到一個類對象,但是用了不同的名字。但是到後來當程式運作的時候,他們有需要這些對象都是同一個對象。這就需要用到單例模式。

單例模式是保證一個類僅有一個執行個體的設計模式。

單例模式保證了程式在不同位置都可以且盡可以取到同一個對象的執行個體:如果執行個體不存在,則會建立一個執行個體;如果對象存在,則會傳回這個實力。

建立單例模式之前,我們要先讨論一下 __new__() 方法。同 __init__() 方法一樣,__new__() 方法也是在建立對象時自動運作的。不同的是,__init__() 方法用來初始化對象,而 __new()__ 方法用來建立對象。

換句話說,__new__() 方法在對象建立之前就會運作,它是一個類方法。它的傳回值就是新建立的對象。

有了這些基礎,結合我們從前學過的東西,就可以建立一個單例模式的類了:

class Student:
    # 類屬性,用來存放執行個體對象記憶體位址,同時也用來标記類是否已經被執行個體化,初始值為None
    instance = None

    # cls用來指代類,args和kwargs用來接收參數,PyCharm預設填入,不必理會也不必修改
    def __new__(cls, *args, **kwargs):
        # 如果沒有執行個體化,就對其進行執行個體化操作,将執行個體化後的對象儲存到類屬性instance中
        if not cls.instance:
            # 這裡的參數隻放cls,不能放*args和**kwargs
            cls.instance = object.__new__(cls)
        # 不管開始的instance是否為空,此時它都已經儲存了一個類對象,将其傳回即可
        return cls.instance

    def __init__(self, name, age):
        self.name = name
        self.age = age


xiaoli = Student('小麗', 16)
xiaoming = Student('小明', 18)
print(xiaoli.name)
print(xiaoming.name)           

輸出結果為:

小明
小明           

需要注意的是,雖然不會建立新的執行個體對象,但是每當建立新對象時,初始化方法 __init__() 還是會執行的。因為是同一個對象,是以後面建立的執行個體屬性就會覆寫從前建立的執行個體屬性。

因為單例模式太過常用且重要,是以我們往往将其單獨寫成一個類。繼承這個單例模式類的新類也都會是單例模式:

class Singleton:
    instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = object.__new__(cls)
        return cls.instance

class Student(Singleton):
    def __init__(self, name, age):
        self.name = name
        self.age = age


xiaoli = Student('小麗', 16)
xiaoming = Student('小明', 18)
print(xiaoli.name)
print(xiaoming.name)           

工廠模式

我們現在有這樣一個需求:我們玩王者榮耀,想要一個實體傷害,射程遠,可以持續輸出的英雄。如果我們自己去找,很慢而且很麻煩。需要我們周遊所有人物的職業,然後才能确定射手類是我們想要的。

但是我們其實可以把這件事情交給程式來做。隻要我們提出需求(實體傷害,射程遠,持續輸出),程式自動給我們傳回一個射手類的對象。這樣就很友善了。

這個幫我們建立類的類就是一個工廠類。這種程式設計模式,就是工廠模式。

工廠模式就是不直接暴露對象的建立細節,而是通過一個共同的類來建立對象的模式,總共需要四個步驟:

  1. 建立基類
  2. 建立子類
  3. 建立工廠類
  4. 使用工廠模式

事實上,工廠類中待我們選擇的類并不要求非要繼承自同一個父類。而且其實 Python 中所有類都繼承自 project 類。是以前兩步可以合并為一步,建立類。

我們可以這樣建立一個工廠模式的類:

# 建立基類
class Person(object):
    def __init__(self, name):
        self.name = name
    def get_name(self):
        return self.name

# 建立子類
class Male(Person):
    def __str__(self):
        return 'Hello Mr.' + self.get_name()
class Female(Person):
    def __str__(self):
        return 'Hello Miss' + self.get_name()
    
# 建立工廠類
class Factory(object):
    def get_person(self, name, gender='M'):
        if gender == 'M':
            return Male(name)
        if gender == 'F':
            return Female(name)
        
# 使用工廠模式
factory = Factory()
person = fcatory.get_person('Bob', 'M')
print(person)