函數與類:如何選擇使用哪個?為什麼?

更新於 發佈於 閱讀時間約 10 分鐘

在程式設計中,我們通常會遇到一個問題: 何時應該使用具有方法的類別,何時應該在模組中使用函式? 如果選擇不當,你的程式碼可能比需要的更複雜,這使得維護更困難

首先,我們需要更好地理解函式和類別的差異。

  • 函式接收輸入參數,然後進行一些操作,並返回結果。你可以將這個結果傳遞給其他函式。在像Haskell這樣的函式語言中,甚至允許你將函式傳遞給函式,或讓函式返回其他函式。(Python 中的 Callable)
  • 在面向對象(物件導向)的程式語言,如Java,一切都圍繞著狀態(物件的狀態)。你有一些變數被組織成對象,這些對象是其他對象的一部分,形成一種階層結構。你甚至可以使用繼承來擴展這些對象結構的定義。
raw-image

本文靈感來自:Functions vs Classes: When to Use Which and Why
以下範例程式均為 Python

當你的程式碼以行動為重點時,請使用函式

在這個例子中,我們有一個簡單的函式來分析數據:

def load_data(file_path):
# 載入數據的實作...
pass

def analyze_data(data):
# 分析數據的實作...
pass

def print_results(results):
# 打印結果的實作...
pass

# 使用函式
data = load_data('data.txt')
results = analyze_data(data)
print_results(results)

以一個分析數據的簡單應用程序為例,這是一個典型的以行動為主導的程序。我們從載入數據開始,然後進行分析,然後再進行第二次分析,並打印該信息。在這種情況下,函式是一種很好的組織程式碼的方式。

當你的程式碼以狀態為重點時,請使用類別

在這個例子中,我們有一個模擬銀行帳戶的類別:

class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance

def deposit(self, amount):
self.balance += amount

def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient balance!")
self.balance -= amount

銀行帳戶代表著一種狀態,並且我們可以創建該類的多個對象,即多個銀行帳戶。我們可以進行存款和提款操作,這些操作都會改變帳戶的狀態:

account = BankAccount(1000)
account.deposit(500)
account.withdraw(200)

更複雜的範例(多個帳戶互相操作)

class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance

def deposit(self, amount):
self.balance += amount

def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient balance!")
self.balance -= amount

def transfer(self, other_account: BankAccount, amount):
"""從此帳戶轉移金額到另一個帳戶"""
if amount > self.balance:
raise ValueError("Insufficient balance!")
self.withdraw(amount)
other_account.deposit(amount)

# 創建兩個帳戶
account1 = BankAccount(1000)
account2 = BankAccount(500)

# 從 account1 轉移 200 到 account2
account1.transfer(
other_account=account2,
amount=200
)

如果我們用函式來解決這個問題,將會更為複雜,因為銀行帳戶更注重狀態而非行動。這就是一種更適合用面向對象程式設計的情況。

的多個對象,即多個銀行帳戶。如果我們用函式來解決這個問題,將會更為複雜,因為銀行帳戶更注重狀態而非行動。這就是一種更適合用面向對象程式設計的情況。

我和其他的文章不一樣,既然說不好,就是要舉例啊
以下是使用函式的範例

使用函式來實現銀行帳戶互轉的功能,可以把帳戶的餘額存放在一個 dictionary 中,然後設計一些函式來對這些帳戶進行操作。以下是一個可能的實現:

def create_account(accounts, name, initial_balance):
accounts[name] = initial_balance

def deposit(accounts, name, amount):
accounts[name] += amount

def withdraw(accounts, name, amount):
if amount > accounts[name]:
raise ValueError("Insufficient balance!")
accounts[name] -= amount

def transfer(accounts, from_name, to_name, amount):
if amount > accounts[from_name]:
raise ValueError("Insufficient balance!")
withdraw(accounts, from_name, amount)
deposit(accounts, to_name, amount)

# 創建兩個帳戶
accounts = {}
create_account(accounts, 'account1', 1000)
create_account(accounts, 'account2', 500)

# 從 account1 轉移 200 到 account2
transfer(accounts, 'account1', 'account2', 200)

這種設計的優點和缺點:

優點:

  • 簡單:這種設計很簡單,我們只需要一個 dictionary 和一些操作帳戶的函式。
  • 與語言無關:這種設計可以在不支持面向對象程式設計的語言中使用。

缺點:

  • 難以管理複雜的情況:如果我們需要管理更複雜的帳戶,例如支持信用卡帳戶和儲蓄帳戶,那麼使用這種設計就變得更困難。我們可能需要添加更多的函式,或者更改現有的函式,這可能導致程式碼變得難以維護。
  • 無法利用面向對象的特性:這種設計無法利用面向對象程式設計的一些特性,例如封裝、繼承和多型性。例如,我們無法為帳戶添加新的屬性或方法,除非我們更改所有的函式。

看起來好像也沒有複雜很多對吧
對!因為以太坊區塊鏈的貨幣轉移就是這樣運作的,人家運做的很好啊
但假設你考慮進去很多因素:

ex

  • 一個帳號有台幣/美金/日圓
  • 每天有提領上限
  • 有 VIP 客戶有特殊權限

可能就會覺得用函數去發想有點複雜了吧

這邊我覺得任何程式的寫法都是一種取捨
沒有一定好壞之分
只有在你的情況下,哪種寫法比較適合你

總的來說,如果你的應用程式足夠簡單,使用函式來實現銀行帳戶互轉可能是一種好的選擇。然而,如果你需要管理更複雜的帳戶,或者你想要利用面向對象程式設計的特性,那麼使用類別可能是更好的選擇。

當然,Python 等語言可以靈活地使用這兩種方法。在許多情況下,使用函式和類別的組合會更有意義。如果你的程序更關注信息的流動,那麼使用更多的函式會更好;如果你的程序更關注狀態,那麼使用更多的類別會更好。
但也別忘了,實驗並嘗試不同的方法,可以幫助你更好地理解,並最終獲得更好的結果。
我是很喜歡用不同方式寫同一種東西啦,但在工作上有時間壓力又是另一回事了!

留言
avatar-img
留言分享你的想法!
Karen-avatar-img
2023/08/18
感謝分享!
avatar-img
我人生遊戲的通關討論區
32會員
88內容數
對我來說 人生就是一個遊戲 活得開心,活得漂亮,活得成功,活得有意義 都是這場遊戲的一個個任務 我想要把這個遊戲打通關 在這裡我會分享一些我自己的經驗 把遊戲打通關的一些技巧 打通關的過程 和我自己發現的小 bug,或捷徑 遇到的喜怒哀樂 遇到的困難 遇到的挫折 歡迎大家一起來摸透和想受 這場人生遊戲
2023/08/05
在Python的typing模組中,NewType用來創建新的類型,其實是一個現有類型的變體。這對於型別檢查(Type Checking)非常有用,能夠幫助我們更清楚地理解我們的代碼和預期的行為。
Thumbnail
2023/08/05
在Python的typing模組中,NewType用來創建新的類型,其實是一個現有類型的變體。這對於型別檢查(Type Checking)非常有用,能夠幫助我們更清楚地理解我們的代碼和預期的行為。
Thumbnail
2023/08/03
方法鏈接和流暢接口在許多現代編程語言和框架中都有使用。這兩個概念有時互相重疊,因為流暢接口通常使用方法鏈接來實現。使用這些技巧可以提高程式碼的可讀性和維護性,使得編碼更符合人類語言的結構。這對於在專案中協同工作的團隊尤為重要,因為它可以讓每個人更容易理解和使用代碼。
Thumbnail
2023/08/03
方法鏈接和流暢接口在許多現代編程語言和框架中都有使用。這兩個概念有時互相重疊,因為流暢接口通常使用方法鏈接來實現。使用這些技巧可以提高程式碼的可讀性和維護性,使得編碼更符合人類語言的結構。這對於在專案中協同工作的團隊尤為重要,因為它可以讓每個人更容易理解和使用代碼。
Thumbnail
2023/06/15
本文靈感來自:How to Document Your Code Like a Pro 以下範例程式均為 Python 推薦插件:better comment 前言 程式碼的有效文件化是編程最重要的部分之一 Python 提升可讀性有以下方式: 註釋 類型提示 文檔字串 這些實踐可以提高程式碼的可讀
Thumbnail
2023/06/15
本文靈感來自:How to Document Your Code Like a Pro 以下範例程式均為 Python 推薦插件:better comment 前言 程式碼的有效文件化是編程最重要的部分之一 Python 提升可讀性有以下方式: 註釋 類型提示 文檔字串 這些實踐可以提高程式碼的可讀
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
介紹朋友新開的蝦皮選物店『10樓2選物店』,並分享方格子與蝦皮合作的分潤計畫,註冊流程簡單,0成本、無綁約,推薦給想增加收入的讀者。
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
當你邊吃粽子邊看龍舟競賽直播的時候,可能會順道悼念一下2300多年前投江的屈原。但你知道端午節及其活動原先都與屈原毫無關係嗎?這是怎麼回事呢? 本文深入探討端午節設立初衷、粽子、龍舟競渡與屈原自沉四者。看完這篇文章,你就會對端午、粽子、龍舟和屈原的四角關係有新的認識喔。那就讓我們一起解開謎團吧!
Thumbnail
我們將探索函式的定義和調用,這是程式設計中非常重要且強大的概念,它可以將大型程式切割成小的、可重複使用的函式。讓我們一起來了解吧!函式的定義、呼叫和返回值是學習函式的核心。
Thumbnail
我們將探索函式的定義和調用,這是程式設計中非常重要且強大的概念,它可以將大型程式切割成小的、可重複使用的函式。讓我們一起來了解吧!函式的定義、呼叫和返回值是學習函式的核心。
Thumbnail
在程式設計中,我們通常會遇到一個問題: 何時應該使用具有方法的類別,何時應該在模組中使用函式? 如果選擇不當,你的程式碼可能比需要的更複雜,這使得維護更困難 首先,我們需要更好地理解函式和類別的差異。 函式接收輸入參數,然後進行一些操作,並返回結果。你可以將這個結果傳遞給其他函式。在像Haskell
Thumbnail
在程式設計中,我們通常會遇到一個問題: 何時應該使用具有方法的類別,何時應該在模組中使用函式? 如果選擇不當,你的程式碼可能比需要的更複雜,這使得維護更困難 首先,我們需要更好地理解函式和類別的差異。 函式接收輸入參數,然後進行一些操作,並返回結果。你可以將這個結果傳遞給其他函式。在像Haskell
Thumbnail
介紹 在C#2.0時代以前,是沒有泛型的,所以當我們遇到需求是方法內做相同的事情,但因為輸入或輸出的型別不一樣,我們就必須重複寫出類似的程式 以下的例子是=>不同的輸入型別 但卻做相同的事情 =>印出輸入資料 範例: 輸入參數:int或string或DateTime 功能:印出輸入的"數值"
Thumbnail
介紹 在C#2.0時代以前,是沒有泛型的,所以當我們遇到需求是方法內做相同的事情,但因為輸入或輸出的型別不一樣,我們就必須重複寫出類似的程式 以下的例子是=>不同的輸入型別 但卻做相同的事情 =>印出輸入資料 範例: 輸入參數:int或string或DateTime 功能:印出輸入的"數值"
Thumbnail
這一篇文章將會從Microsoft的.NET Class Library開始介紹,在介紹具狀態和不具狀態,以及物件和new關鍵字。
Thumbnail
這一篇文章將會從Microsoft的.NET Class Library開始介紹,在介紹具狀態和不具狀態,以及物件和new關鍵字。
Thumbnail
在智能合約的函數裡面,最常用的就是檢查使用者是不是合約的Owner: 這樣寫的話,每個需要檢查是否為Owner的函數就必須都要這樣寫入,為了程式碼的簡潔和提高重覆利用,我們來使用modifier看看: 上面我們是看到了檢查的部份,那為什麼叫修改器呢?下面讓我們來看看:
Thumbnail
在智能合約的函數裡面,最常用的就是檢查使用者是不是合約的Owner: 這樣寫的話,每個需要檢查是否為Owner的函數就必須都要這樣寫入,為了程式碼的簡潔和提高重覆利用,我們來使用modifier看看: 上面我們是看到了檢查的部份,那為什麼叫修改器呢?下面讓我們來看看:
Thumbnail
撰寫程式時,有沒有想過宣告的這些變數是怎麼運作的呢? 本文介紹了Value Type跟Reference Type,以及Variable的基本命名方式。
Thumbnail
撰寫程式時,有沒有想過宣告的這些變數是怎麼運作的呢? 本文介紹了Value Type跟Reference Type,以及Variable的基本命名方式。
Thumbnail
隨著程式的功能愈來愈複雜,程式碼也愈來愈多,若程式從頭寫到尾沒有任何的段落,可讀性會愈來愈差,甚至會發現同樣的一段程式碼重覆很多遍,因為類似的功能區塊在程式中可能會一再出現。這樣的程式碼不利於多人的協作開發,即使是寫作者本身在一段時間後回來看,可能也難以一眼就掌握程式的主要架構。
Thumbnail
隨著程式的功能愈來愈複雜,程式碼也愈來愈多,若程式從頭寫到尾沒有任何的段落,可讀性會愈來愈差,甚至會發現同樣的一段程式碼重覆很多遍,因為類似的功能區塊在程式中可能會一再出現。這樣的程式碼不利於多人的協作開發,即使是寫作者本身在一段時間後回來看,可能也難以一眼就掌握程式的主要架構。
Thumbnail
不間斷 Python 挑戰 Day 2 - 註解、變數與資料型態
Thumbnail
不間斷 Python 挑戰 Day 2 - 註解、變數與資料型態
Thumbnail
int main()、註解//、include 、命名空間、using namespace
Thumbnail
int main()、註解//、include 、命名空間、using namespace
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News