python常用設計模式解析


一. 什么是python的設計模式?

  軟件工程中,設計模式是指軟件設計問題的推薦方案。設計模式一般是描述如何組織代碼和使用最佳實踐來解決常見的設計問題。需謹記一點:設計模式是高層次的方案,並不關注具體的實現細節,比如算法和數據結構。對於正在嘗試解決的問題,何種算法和數據結構最優,則是由軟件工程自己把握。面試經常會問到設計模式,所以我給大家准備一些常用的設計模式,也可以更好的與面試官交流。

 

二. python實現設計模式

設計模式共分為三大類,細分為23種設計模式。

  • 創建型模式
  • 結構型模式
  • 行為型模式

 

2.1創建型模式

單例模式

單例模式(Singleton Pattern)是一個常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在,當希望在某個系統中只出現一個實例時,單例對象就能派上用場。比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。

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

s1 = Singleton()
s2 = Singleton()
print(id(s1),id(s2))
單利模式代碼構建

工廠模式

工廠模式是一個在軟件開發中用來創建對象的設計模式。

在工廠模式設計中,客戶端可以請求一個對象,而無需知道這個對象來自哪里(使用類來生成對象)也就是說,客戶只需要調用指定的函數或者方法,傳入具有辨別性質的參數(如下面代碼的"M","F")得到的返回值就是最終的對象。工廠背后的思想是簡化對象的創建,基於一個中心化的函數(定義一個抽象的實例化對象的接口)來實現創建對象,(類似於我在面向對象的類的約束中講過的歸一化設計)通過將創建對象的代碼和使用對象的代碼解耦,工廠能夠降低應用維護的復雜度。

class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def get_name(self):
        return self.name

    def get_gender(self):
        return self.gender


class Male(Person):
    def __init__(self, name, gender):
        super().__init__(name, gender)
        print("Hello Mr." + self.name)


class Female(Person):
    def __init__(self, name, gender):
        super().__init__(name, gender)
        print("Hello Miss." + name)

if __name__ == '__main__':
    
    obj1 = Male('太白','male')
    obj3 = Male('瑋哥', 'male')
    obj2 = Female('小花', 'female')
    
    print(obj1.get_name())
    print(obj1.get_gender())
    
    print(obj2.get_gender())
    
    print(obj3.get_gender())
沒使用工廠模式的代碼示例
class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def get_name(self):
        return self.name

    def get_gender(self):
        return self.gender


class Male(Person):
    def __init__(self, name, gender):
        super().__init__(name, gender)
        print("Hello Mr." + self.name)


class Female(Person):
    def __init__(self, name, gender):
        super().__init__(name, gender)
        print("Hello Miss." + name)

class Factory:

    def get_person(self, name, gender):  # 統一了實例化對象的接口

        if gender == 'male':
            return Male(name, gender)
        else:
            return Female(name, gender)


if __name__ == '__main__':
    factory = Factory()

    # 每次實例化對象時調用統一的接口即可
    p1 = factory.get_person("太白", "male")
    p2 = factory.get_person("小花", "female")
    p3 = factory.get_person("瑋哥", "male")

    p1.get_name()
    p1.get_gender()

    p2.get_name()
    p2.get_gender()

    p3.get_name()
工廠模式代碼示例

 建造者模式

將一個復雜對象的構建與他的表示分離,使得同樣的構建過程可以創建不同的表示。在該模式中,有兩個參與者:建造者(builder)和指揮者(director)。建造者負責創建復雜對象的各個組成部分,指揮者負責制定對象特定的執行順序。
示例:
1.有一個接口類,定義創建對象的方法。一個指揮員類,接受創造者對象為參數(組合)。兩個創造者類,創建對象方法相同,內部創建可自定義
2.一個指揮員,兩個創造者(瘦子 胖子),指揮員可以指定由哪個創造者來創造。

from abc import ABCMeta, abstractmethod


class Builder(metaclass=ABCMeta):
    # 接口類,制定規范,只要此類必須要設置以下的被abstractmethod裝飾的這些方法。
    @abstractmethod
    def draw_arm(self):
        pass

    @abstractmethod
    def draw_foot(self):
        pass

    @abstractmethod
    def draw_head(self):
        pass

    @abstractmethod
    def draw_body(self):
        pass

class Thin(Builder):

    def draw_arm(self):
        print("畫手")


    def draw_foot(self):
        print("畫腳")

    def draw_head(self):
        print("畫頭")

    def draw_body(self):
        print("畫瘦身體")


class Fat(Builder):

    def draw_arm(self):
        print("畫手")

    def draw_foot(self):
        print("畫腳")

    def draw_head(self):
        print("畫頭")

    def draw_body(self):
        print("畫胖身體")

class Director():
    def __init__(self, person):
        self.person = person

    def draw(self):
        self.person.draw_arm()
        self.person.draw_foot()
        self.person.draw_head()
        self.person.draw_body()

if __name__ == '__main__':
    thin = Thin()
    fat = Fat()
    director_thin = Director(thin)  # 組合
    director_thin.draw()  # 按照指揮者的類的draw方法順序執行
    director_fat = Director(fat)  # 組合
    director_fat.draw()  # 按照指揮者的類的draw方法順序執行
建造者模式代碼示例

原型模式

用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
原型模式本質是克隆對象,所以在對象初始化操作比較復雜的情況下,很實用,能大大降低耗時,提高性能。因為“不用重新初始化對象,而是動態獲得對象運行的狀態”。屬於副本的概念,與引用不同。

引用就是多個變量共同指向一個數據的內存地址(變量多次賦值)。副本就是重新創建一份。

原型模式示例:某一本書在初版之后又經歷了一次改版,對書名、價錢、頁碼等進行了修改。但是這次修改並不是重新裝訂了一本書,只是修改了部分內容,這時候就不需要重新創建一個對象,只需要把原來的對象的副本內容做一些修改。

import copy
from collections import OrderedDict


class Book:
    def __init__(self, name, authors, price, **rest):
        '''rest的例子有:出版商、長度、標簽、出版日期'''
        self.name = name
        self.authors = authors
        self.price = price  # 單位為美元
        self.__dict__.update(rest)

    def __str__(self):

        return ''.join([f'{k}: {v}\n' if k != "price" else f'{k}: {v}$\n' for k, v in self.__dict__.items()])



class Prototype:
    def __init__(self):
        self.objects = dict()

    def register(self, identifier, obj):
        self.objects[identifier] = obj

    def unregister(self, identifier):
        del self.objects[identifier]

    def clone(self, identifier, **attr):
        found = self.objects.get(identifier)
        if not found:
            raise ValueError('Incorrect object identifier: {}'.format(identifier))
        obj = copy.deepcopy(found)  # 深copy一個對象
        obj.__dict__.update(attr)
        return obj


def main():
    b1 = Book('太白教你學python', ('太白金星', '老司機'), price=118, publisher='IT出版社', publication_date='2014-08-22',
              tags=('IT', 'programming', 'py', '人工智能'))

    prototype = Prototype()

    cid = 'first'

    prototype.register(cid, b1)

    b2 = prototype.clone(cid, name='Django框架深度剖析', price=129.9, publication_date='2016-04-01',
                         edition=2)
    # 通過克隆b1對象的方式創建b2對象,這樣避免了實例化對象b1時的復雜操作,提升性能,很實用。
    for i in (b1, b2):
        print(i)
    print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))


if __name__ == '__main__':
    main()
原型模式代碼示例

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM