1. 程式人生 > >一文詳解“工廠模式”以及python語言的實現

一文詳解“工廠模式”以及python語言的實現

一、什麼是“工廠模式”——factory pattern

工廠模式,也稱之為“簡單工廠模式”或者是“靜態工廠模式

工廠模式(Factory Pattern)是 程式設計中 中最常用的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。在工廠模式 中,我們在建立物件時不會對客戶端暴露建立邏輯,所謂的“建立邏輯”是指我必須要知道建立函式的建構函式的邏輯組成,才能建立物件。

比如:

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

student=Student('張三',23)

在上面的例子中,要想建立一個學生物件,我就必須要知道學生的 姓名 、年齡 這兩項基本資訊才能建立,這兩個資訊,就是所謂的建立邏輯,在實際中,可能知道的資訊還要更多我才能建立一個例項,因為在同齡人中 姓名也相同的大有人在,我還需要知道他多高,性別、喜歡什麼、籍貫等等才能完全建立一個學生例項。那有沒有辦法在不知道學生姓名和這些資訊的情況下建立一個學生例項呢?這就是工廠模式要做的事情。

所謂”工廠模式“就是專門提供一個”工廠類“去建立物件,我只需要告訴這個工廠我需要什麼,它就會返回給我一個相對應的物件。

說的更通俗一點,就是專門建立型別的例項的工廠(類) 

二、工廠模式的使用場景

使用場景:您需要一輛汽車,可以直接從工廠裡面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裡面的具體實現。

作為一種建立類模式,在任何需要生成複雜物件的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜物件適合使用工廠模式,而簡單物件,特別是只需要通過 new 就可以完成建立的物件,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。 

總結:如果要建立一系列複雜的物件,需要提供很多的建立資訊;或者是我想要隱藏建立物件時候的“程式碼邏輯”,就需要使用“工廠模式”

優點: 1、一個呼叫者想建立一個物件,只要知道其名稱就可以了,不需要知道其他詳細資訊。 2、作為呼叫者而言,遮蔽產品的具體實現,呼叫者只關心產品的介面。

缺點:當有新的型別要加入到系統中時,必須修改工廠類,加入必要的處理邏輯,這違背了“開閉原則”。在簡單工廠模式中,所有的產品都是由同一個工廠建立,工廠類職責較重,業務邏輯較為複雜,具體產品與工廠類之間的耦合度高,嚴重影響了系統的靈活性和擴充套件性,而工廠方法模式則可以很好地解決這一問題。(工廠方法模式和工廠模式是有區別的哦)

使用例項: 1、日誌記錄器:記錄可能記錄到本地硬碟、系統事件、遠端伺服器等,使用者可以選擇記錄日誌到什麼地方。 2、資料庫訪問,當用戶不知道最後系統採用哪一類資料庫,以及資料庫可能有變化時。 3、設計一個連線伺服器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個介面。

三、工廠模式的python程式碼實現

比如我需要建立很多的圖形物件,這些圖形包括三角形、圓、矩形、橢圓等等,當然我們可以使用每一個型別的建構函式去建立。但是我們不這樣做,我們只需要專門建立一個“工廠類”,然後在需要建立某個例項的時候告訴它我需要什麼類,就可以啦,如下程式碼所示:

import math

#定義4個圖形類,並且每一個圖形都有一個可以計算面積的方法
class Circle:
    def Area(self,radius):
        return math.pow(radius,2)*math.pi

class Rectangle:
    def Area(self,longth,width):
        return 2*longth*width

class Triangle:
    def Area(self,baselong,height):
        return baselong*height/2

class Ellipse:
    def Area(self,long_a,short_b):
        return long_a*short_b*math.pi

#=================================
#定義建立物件的一個工廠
class Factory:
    def create_shape(self, name):
        if name =='Circle':
            return Circle()
        elif name == 'Rectangle':
            return Rectangle()
        elif name == 'Triangle':
            return Triangle()
        elif name == 'Ellipse':
            return Ellipse()
        else:
            return None

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

    circle=factory.create_shape('Circle')
    circle_area=circle.Area(2)
    print(f'這是一個圓,它的面積是:{circle_area}')

    rectangle=factory.create_shape('Rectangle')
    rectangle_area=rectangle.Area(2,3)
    print(f'這是一個長方形,它的面積是:{rectangle_area}')

    triangle=factory.create_shape('Triangle')
    triangle_area=triangle.Area(2,3)
    print(f'這是一個三角形,它的面積是:{triangle_area}')

    ellipse=factory.create_shape('Ellipse')
    ellipse_area=ellipse.Area(3,2)
    print(f'這是一個橢圓,它的面積是:{ellipse_area}')

上面程式碼的執行結果為:

這是一個圓,它的面積是:12.566370614359172
這是一個長方形,它的面積是:12
這是一個三角形,它的面積是:3.0
這是一個橢圓,它的面積是:18.84955592153876

從上面可以看出,我們在建立某一個圖形類的時候,根本不關心這個類的建構函式到底是什麼,甚至它都沒有自定義的建構函式,這並不影響,我們只需要告訴工廠,你給我建立一個什麼類即可,除了這點資訊以外,不需要知道任何的額外資訊,這就是所謂的“隱藏建立類的程式碼邏輯” 。

但是上面的方法依然有一個缺點:

簡單工廠模式實現了生成產品類的程式碼跟客戶端程式碼分離,在工廠類中你可以新增所需的生成產品的邏輯程式碼,但是問題來了,優秀的java程式碼是符合“開放-封閉”原則的,也就是說對擴充套件開發,對修改關閉,如果你要加一個產品類C,你就要修改工廠類裡面的生成產品的程式碼,在這裡你就要增加if-else判斷。對於這個問題,我們的工廠方法模式就可以解決這個問題。

四、簡單工廠模式所存在的問題

根據程式設計的“開放-封閉原則”。即所謂的對擴充套件開放,對修改關閉。就是如果我想要拓展一個類中的相應功能,是完全可以的,我只需要邊編寫專門的拓展模組,並不需要修改原來類中的任何程式碼。(如果學過C#,裡面有一種拓展方法,就是專門針對這種情況的)。

簡單工廠模式實現了生成產品類的程式碼跟客戶端程式碼分離,(這裡的生成產品類指的就是上面例子中的那些圖形類,客戶端指的是需要呼叫上面圖形類種方法或屬性的程式設計師)。在工廠類中你可以新增所需的生成產品的邏輯程式碼,但是問題來了,優秀的程式碼是符合“開放-封閉”原則的,也就是說對擴充套件開放,對修改關閉,如果你要加一個圖形類,比如平行四邊形類,要使得依然能夠通過工廠型別建立物件,就要修改工廠類裡面的產生物件的程式碼,在這裡你就要增加if-else判斷,需要修改原來工廠函式的程式碼,這不就是要修改原始碼嗎?這就一定程度上又違背了“開放-封閉原則”。

對於這個問題,我們的工廠方法模式就可以解決這個問題。(請參見下一篇)

補充:上面的“工廠類”對於像C#和java這樣的語言來說,是必備的,因為它們的函式是依託於類的,但是因為python是特別靈活的語言,我不需要工廠類Factory,直接用一個函式代替也是完全沒有問題的。如下程式碼,當然,根據設計模式的原則,最好還是建立一個工廠類Factory,然後讓這個類裡面的建立函式去建立類。

直接用一個函式代替工廠類,如下:

def create_shape(name):
        if name =='Circle':
            return Circle()
        elif name == 'Rectangle':
            return Rectangle()
        elif name == 'Triangle':
            return Triangle()
        elif name == 'Ellipse':
            return Ellipse()
        else:
            return None

if __name__=='__main__':

    circle=create_shape('Circle')
    circle_area=circle.Area(2)
    print(f'這是一個圓,它的面積是:{circle_area}')

上面我直接用一個函式代替工廠類也是可以的。