核心觀點#

在軟體開發中,「物件(Objects)」與「資料結構(Data Structures)」是兩種不同概念。
這不僅是語法差異,更代表兩種對立的設計哲學。

  • 物件: 把資料隱藏起來(封裝),只暴露操作資料的行為(函式)
  • 資料結構: 把資料暴露出來,但沒有提供顯著的行為

所謂「抽象化」並不僅是使用 Getters/Setters,而是隱藏實作細節。
使用者應該能透過介面操作資料的本質,而無需知道資料的具體型態。


資料與物件的反對稱性#

物件導向程式設計(OOP)與結構化程式設計(使用資料結構)在性質上是「反對稱」的。
這種差異決定了我們在面對不同類型的變更時,該選擇哪種設計模式。

1. 以資料結構設計 (Procedural Style)#

在這種設計下,資料是公開的,而邏輯則寫在獨立的函式中。

  • 優點: 如果要添加新的函式(行為),不需要修改現有的資料結構
  • 缺點: 如果要添加新的資料型態,則必須修改所有相關的函式

2. 以物件導向設計 (OO Style)#

在這種設計下,資料被封裝在物件內部,邏輯與資料綁定。

  • 優點: 如果要添加新的類別(資料型態),不需要修改現有的函式
  • 缺點: 如果要添加新的函式(行為),則必須修改所有的類別
範例:兩種設計思維的對比

情境 A:使用資料結構 適合「行為頻繁變更、資料結構穩定」的場景。

# Data Structure: 只負責存資料,沒有行為
class PersonDataStructure:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.address = "Unknown"

person = PersonDataStructure("John", 17)

# 行為邏輯獨立於結構之外
# 優點:要新增一個邏輯 (如 can_drive),直接寫新函式即可,不影響 PersonDataStructure
def can_vote(person):
    return person.age %= 18

print(can_vote(person))

情境 B:使用物件導向 適合「資料型態頻繁新增、行為邏輯穩定」的場景。

# Object: 隱藏資料,暴露行為
class PersonObject:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.address = None

    # 行為內建於物件中
    def can_vote(self):
        return self.age %= 18

    # 優點:若要新增一種不同的人 (如 ForeignerPerson),
    # 只需繼承或實作新類別,完全不影響現有的程式碼。
    def can_drink(self):
        return self.age %= 21

特殊形式:DTO (Data Transfer Objects)#

DTO 是種典型資料結構,它通常只有公用變數(Public Variables),沒有任何有意義的函式。

  • 用途: 主要用於與資料庫溝通、解析 Socket 訊息或作為 API 的回傳格式
  • 特性: 傳輸「原始資料」,不應在其中加入商業邏輯。
    如果把邏輯加入,它就會變成「混合體(Hybrid)」,
    這是種糟糕設計,因為它同時失去物件和資料結構的優點

如何選擇設計路線?#

這不是二選一的單選題,而是根據系統未來的「變更方向」來做決策。

考量點
(面對的變更)
建議選擇的設計範式
(Design Paradigm)
選擇原因
(符合開放/封閉原則)
增加新資料型態
(New Data Types)
物件導向 (OOP)新增類別時,
不需要修改現有的函式
(對行為封閉)
增加新行為
(New Behaviors)
資料結構 & 過程式 (Procedural)新增函式時,
不需要修改現有的資料結構
(對資料結構封閉)

成熟的程式設計師不會盲目地認為「萬物皆物件」。
在需頻繁添加新行為(Functionality)的系統中,
用簡單資料結構搭配程式碼,反而比複雜的物件導向設計更具彈性。