【程式優化】OOP (物件導向程式設計、Object Oriented Programming) - SRP (單一職責原則、Single Responsibility Principle)

介紹 OOP 設計原則中的 SRP (Single Responsibility Principle) — 每個函數職責單一,降低修改時的系統影響範圍。

前言

在程式碼優化中,我們可以想像如果我們能將每一個功能拆的非常的精細
(例如一個函數 (function) 只會有屬於他的單一功能)

這部份也是 OOP (物件導向程式設計、Object Oriented Programming) 所強調的精神之一
“S"OLID 物件導向設計原則中的 “S” 指的就是 Single Responsibility。

以個人經驗來說,可以得到以下的好處:

    - 除錯(debug) 單一功能的問題:只需針對單一函數 (function) 去做 debug - 維護(maintain) 程式碼的單一功能:只需維護單一函數 (function) - 修改(modify) 程式碼的單一功能:只需修改單一函數 (function) - 程式碼可讀性 (readability) :可以明確知道此函數就是針對單一功能去做撰寫,在大型系統系統中如果一個函數做太多功能,「別的工程師」在閱讀時也必須去了解此函數中更多程式碼的細節。 - 程式碼可重用性 (reusability):如果每一個功能都拆的很乾淨,有時類似功能的單一函數相對容易做重複使用。

還有很多的好處,雖然以上看起來像在講廢話(欸),
實際上要去「實踐」這件事與「了解」這件事情完全是兩回事XD,
我自己也正努力在「優化程式碼」的學習路上。

SRP (單一職責原則、Single Responsibility Principle)

原文 (Robert C. Martin)

A class should have only one reason to change

自己的解釋

降低單一功能當需要「改變」時,影響整體系統的機會。

講完了。你說「蛤?」
沒辦法這概念就這麼抽象XDDD,試著舉一個例子也許比較好懂。

我想要定義一支狗的類別:

class Dogs:
    # 建構式
    def __init__(self, name, sex):
        self.name = name  # 姓名屬性
        self.sex = sex  # 性別屬性

假設我們知道了,「肚子餓」或「受傷」都會讓狗生氣。
我們可以這樣增加功能:

class Dogs:
    # 建構式
    def __init__(self, name, sex):
        self.name = name  # 姓名屬性
        self.sex = sex  # 性別屬性

    # 方法(Method)        
    def hungry(self):
        print("The dog is angry.")

    def hurt(self):
        print("The dog is angry.")  

我們可以看到不論是 「hungry(self)」, 「hurt(self)」,
都會觸發狗生氣的反應。

但是上面的寫法並沒有運用到 SRP 的觀念
我們假設我們想要增加狗「生氣」時會有的其他反應,
有沒有發現同樣的功能我們可能需要改兩次一樣的東西?

那我們在仔細想一下,現實中會有更多種可能會觸發狗「生氣」的方法,
上面的方法雖然寫的快速又直接明瞭,但相對來說碰到修改「生氣」的功能時,
變成「全部」都要慢慢一個個改。

於是參照SRP 的觀念
我們定義了一個「生氣」的方法,象徵了當狗生氣時可能會有的反應。

class Dogs:
    # 建構式
    def __init__(self, name, sex):
        self.name = name  # 姓名屬性
        self.sex = sex  # 性別屬性

    # 方法(Method)
    def angry(self):
        print("The dog is angry.")

    def hungry(self):
        self.angry()

    def hurt(self):
        self.angry()    

這樣就能夠解決我們上述所說的,當我們想增加「生氣」會有的反應時,
我們不需要全部一個個慢慢改動功能。

也就是 SRP (Single Responsibility Principle) 所提倡的單一職責原則。

使用情境、優缺點

上面的例子中,我們也看到大型程式中,SRP 的效果才會明顯的被展現,
如果只是小程式講究快速開發,也許 SRP 有時候開發效率不見得會高,
但維護功能上,我們也能看出 SRP 在大型系統的重要之處。
我們只需要針對單一函數 「angry(self)」 去進行維護即可。

若沒有使用 SRP ,維護上我們必須一個個函數去查看。
就如同上述例子,我們必須同時修改「hungry(self)」, 「hurt(self)」的功能。
更何況是更大型的系統,未使用 SRP 維護會更缺乏效率的。

Reference

使用 Hugo 建立
主題 StackJimmy 設計