寫代碼也有“套路”-談談設計模式

編程教室開了這麼久,已經有很多人從完全零基礎的小白成爲了會寫代碼的菜鳥程序員,能夠自己獨立開發程序。不過到此階段,常常會遇到瓶頸,感覺功能可以實現,但代碼看起來有些彆扭:

  • 代碼中有很多相似的重複代碼
  • 代碼中有大量的 if,以至於有很長的縮進
  • 單個代碼寫得很長,別人很難看懂,過陣子可能自己都看不懂
  • 總會有沒有考慮到的情況導致 bug
  • 修復一個 bug 又會產生新的 bug
  • 單個功能可以實現,但多個功能組合在一起就理不清
  • 如果需求發生變動,代碼修改起來很麻煩
  • 有多種實現方式時不知道該用哪一種
  • 很難和其他人協作開發

上述的問題你是不是有過類似困擾?解決的辦法其實也簡單,就是堅持“ 多寫 ”和“ 多讀 ”:

  1. 多寫代碼。很多時候你覺得不好處理,並不是因爲高深的問題,只是你對代碼的基本使用還不夠熟練。同樣的問題,踩過坑再爬出來,反覆幾次自然你也知道怎麼繞開了。
  2. 多讀代碼。除了自己寫,看看別人的代碼也會學到很多。包括教程裏的案例、官方示例、開源項目的源碼等。所謂“熟讀唐詩三百首,不會作詩也會吟”嘛。

除了這兩個“笨辦法”外,還有樣東西,對於處在這個階段的你或許有很大啓發,這就是:

設計模式

設計模式是對於軟件開發中常見的一些問題所總結出的解決方案,它並不關注具體的代碼怎麼寫,而是 代碼的結構應該如何設計 ,從而讓代碼 更加可靠、可讀、可重用、易於維護 。它不是一種嚴格意義上的“技術”,而是一門“經驗主義”,也就是開發者經常提到的“ 最佳實踐 ”。所以設計模式其實就是在前人各種踩坑經驗之上,總結出的各種開發“套路”。

舉幾個常見的設計模式例子:

單例模式

場景舉例:代碼中需要一個共享的資源管理器,保證在代碼只有唯一的一個實例,且代碼各處都可以訪問到。

如果你的代碼只有一個文件,可能不會遇上這個問題。但當項目大一點之後,這個問題十分常見。你可以選擇定義一些全局變量來實現。但更好的“套路”是使用單例模式:它可以保證 只創建一個對象 (第一次訪問時創建,之後訪問時直接返回已有對象), 並提供全局的訪問

代碼演示



# 單例類
class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kw):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
        return cls._instance

# 繼承單例
class MyClass(Singleton):  
    a = 1

# 使用
mc1 = MyClass()
mc2 = MyClass()
print(mc1==mc2)
mc1.a += 1
print(mc2.a)

輸出



True
2

之前我們的文章《Crossin:Python單例模式(Singleton)的N種實現》有專門討論過 Python 中的單例模式實現。

工廠模式

場景舉例:在一個繪圖工具裏,有不同的筆刷。當點擊不同的筆刷按鈕時,需要創建對應的筆刷供使用。

如果把每個按鈕響應裏都去單獨創建,代碼會很冗餘且不利於維護。這時可創建一個“工廠”類,將創建筆刷的代碼放在其中, 只需要根據傳入的參數不同,返回不同的筆刷實例即可 。就如同工廠根據訂單生產產品一樣。

代碼演示



# 筆刷工廠,具體類代碼略
def pen_factory(mode):
    if mode == 'PEN':
        return Pen()
    elif mode == 'PENCIL':
        return Pencil()
    elif mode == 'BRUSH':
        return Brush()
    else:
        return None

# 使用
p = pen_factory('BRUSH')

代理模式

場景舉例:開發一個論壇,允許用戶在帖子中上傳圖片,於是需要提供保存圖片的功能。

一般爲了網站的訪問速度,會選擇第三方的圖片保存服務。那麼在保存圖片的時候,就需要調用其提供的接口。而應用代理模式的話,就會在網站和第三方服務間增加一層。這樣的好處是可以 將一些額外的處理放在代理層中 ,當需要更換第三方服務時, 不需要修改網站的邏輯 ,只要調整代理層即可。

代碼演示



class ImgService:
    @abstractmethod
    def save(self, img):
        pass

# 真實類
class XYZImgService(ImgService):
    def save(self, img):
        # 調用第三方服務接口

# 代理類
class ImgServiceProxy(ImgService):
    def __init__(self):
        self.service = XYZImgService()
    def save(self, img):
        return self.service.save(img)

# 使用
def save_img(img):
    proxy = ImgServiceProxy()
    proxy.save(img)

設計模式的 六大原則

1、 開閉原則 (Open Close Principle)
對擴展開放,對修改關閉。在需求變動時,儘可能不修改原有代碼,而通過擴展實現。

2、 里氏代換原則 (Liskov Substitution Principle)
在使用繼承時,在子類中儘量不要重寫和重載父類的方法。

3、 依賴倒轉原則 (Dependence Inversion Principle)
針對接口編程,細節依賴於抽象。

4、 接口隔離原則 (Interface Segregation Principle)
降低類之間的耦合度,不依賴不必要的接口。

5、 迪米特法則 ,又稱 最少知道原則 (Demeter Principle)
模塊之間相互獨立,對自己依賴的類需要知道的信息越少越好。

6、 合成複用原則 (Composite Reuse Principle)
儘量使用合成/聚合的方式,而不是使用繼承。

具體的解讀,今天沒法在一篇推送中展開詳述,而且這是個需要不斷體會和實踐的事情。首先你可以找些相關書籍讀一讀。設計模式有一本經典書籍:《 Design Patterns: Elements of Reusable Object-Oriented Software 》(《設計模式:可複用面向對象軟件的基礎》)。此書又被稱作“ GoF ”(Gang of Four,四人組),四位業內大牛總結出了 23 種設計模式。

然而,我強烈 推薦新手去看這本書,因爲你很可能看不懂。我推薦的是一本叫做《 Head first 設計模式 》的書,可以算作上面那本的白話入門版,對於新手來說友好許多,或許幫助更大。

開發者對於設計模式的理解大致存在這樣幾個階段:一開始只關注語法和庫, 不懂設計模式 ,寫代碼無章法;後來瞭解了設計模式之後,開始嘗試 套用設計模式 ,懂得重構代碼,但有時難免教條化或陷入過度設計的誤區;等到開發經驗豐富之後, 不再拘泥於所謂的“模式” ,本身寫出的代碼就已經契合設計的原則。

書上的設計模式是一成不變的,不可能涵蓋每一種開發場景,而軟件技術本身也不斷髮展,很多模式已經被新的語言特性所實現。因此也有很多人對於設計模式的價值存在質疑。我的看法是,如果你處在新手階段, 學習設計模式是很好的提升方式 ,也可以開拓你的編程思維。而當你已經走上進階之路之後, 更多的應是關注模式背後的原則 ,寫出更合理的代碼,而並非爲了模式而模式。

當然這一切,還是都離不開足夠的代碼量。每個程序員都是一行行代碼堆出來。

════

其他文章及回答:

學編程:如何自學Python | 新手引導 | 一圖學Python

開發案例:智能防擋彈幕 | 紅包提醒 | 流浪地球

歡迎搜索及關注: Crossin的編程教室

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章