學習 OOP 時,「封裝」這個詞大概會聽上百次,但真的被問到「所以程式碼要怎麼做?」時,卻常常突然說不出話。概念明白,手卻動不了。
先說結論,封裝就是「隱藏物件內部資料,避免外部任意修改,只能透過既定管道存取」。在 Swift 中,這是透過存取修飾詞實作的。
今天會用實際程式碼逐一看看 private、internal 等 5 個層級的存取控制,整理資訊隱藏為什麼能保護程式碼。
封裝到底是什麼?
封裝(Encapsulation)是 OOP 四大特性之一。
用一句話總結如下。
將資料與處理資料的功能綁在一起,隱藏內部實作,只向外公開必要部分。
為什麼要隱藏?
想想銀行帳戶就很容易理解。如果任何人都能直接把帳戶餘額改成任意數字,事情就嚴重了。
必須經過存款與提款等既定流程,並確保「不能提領超過餘額」之類的規則確實被遵守。
像這樣阻止外部直接碰觸內部值,只開放經過驗證的管道,就稱為資訊隱藏。
如果封裝是較大的概念,資訊隱藏就是實現它的核心原則。
Swift 存取控制 5 個層級一覽比較
Swift 將存取範圍分成 5 個層級,以下依範圍由窄到寬整理。
(以 2026 年 Swift 最新語法為準。)
| 存取修飾詞 | 可存取範圍 | 主要用途 |
|---|---|---|
private |
宣告所在的大括號區塊內 | 真正想隱藏的內部狀態 |
fileprivate |
整個相同原始程式檔 | 同一檔案中的協作型別 |
internal |
整個相同模組(App/Framework) | 預設值,大多數程式碼 |
public |
也能從其他模組使用 | 函式庫對外公開的 API |
open |
允許從其他模組繼承與覆寫 | Framework 擴充點 |
這裡有一點一定要記住。
什麼都不加時,internal就是預設值。
所以平常即使不寫存取修飾詞,在同一個 App 裡也能正常運作。
public與open的差異也很容易混淆。在其他模組中,只有open可以進行繼承與覆寫。public雖然可以使用,但不能繼承。
用 private 實現真正的資訊隱藏
光用文字不容易體會,我們直接看帳戶範例的程式碼。
先從沒有封裝的錯誤範例開始。
class BadAccount {
var balance: Int = 0 // 外部任何人都能修改
}
let acc = BadAccount()
acc.balance = -99999 // 不合理的值也會直接寫入
print(acc.balance)
// 輸出: -99999
餘額竟然是負 9 萬,規則整個崩潰了。
現在用private隱藏內部狀態,只開放存提款管道。
class Account {
private var balance: Int = 0 // 阻擋外部存取
func deposit(_ amount: Int) {
guard amount > 0 else { return }
balance += amount
}
func withdraw(_ amount: Int) -> Bool {
guard amount > 0, balance >= amount else { return false }
balance -= amount
return true
}
var currentBalance: Int { balance } // 只允許讀取
}
let myAccount = Account()
myAccount.deposit(10000)
print(myAccount.withdraw(30000)) // 餘額不足
// 輸出: false
現在外部無法直接修改balance。
像myAccount.balance = -99999這樣的程式碼會直接產生編譯錯誤。
資金只能透過deposit與withdraw流動,驗證規則也明確寫在其中。
這就是資訊隱藏的力量:從根本上阻止你寫出違反規則的程式碼。
private(set) — 開放讀取,只禁止寫入
接著來看更實務的情況。
「外部可以讀取值,但只想禁止修改」是非常常見的需求。
像上面一樣另外建立計算屬性當然可以,但 Swift 提供了更簡潔的方法。
class ScoreBoard {
private(set) var score: Int = 0 // 讀取 public, 寫入 private
func addPoint() {
score += 10
}
}
let board = ScoreBoard()
board.addPoint()
print(board.score) // 讀取不受限制
// 輸出: 10
// board.score = 999 // 這行會產生編譯錯誤
加上private(set)後,讀取仍對外開放,但寫入權限會限制在內部。
分數只能透過addPoint()增加,因此能避免遊戲邏輯外部任意操控分數。
我很常用這個語法,因為不需要另外建立計算屬性。
什麼時候該用哪個修飾詞?
整理幾個實務上感到困惑時可以依循的判斷標準。
- **先從
private**開始。需要時再擴大範圍,比一開始全部開放、之後再縮小更安全。 - **預設值
internal**在建立單一 App 時大多都很合適,不必全部明確寫出來。 - **
public・open**只有在建立供他人使用的函式庫或 Framework 時才需要考慮。 - 只有在要開放繼承時才使用**
open。**否則使用public就足夠了。
| 情況 | 判斷 |
|---|---|
| 只在內部使用的狀態變數 | private |
| 只想讓外部讀取 | private(set) |
| 在同一個 App 內自由使用 | internal(預設值) |
| 以函式庫形式對外公開 | public |
| 允許外部繼承與覆寫 | open |
只要記住一件事:存取範圍總是應該盡可能縮小後再開放。
面試時會這樣問
Q. 封裝和資訊隱藏有什麼不同?
封裝是將資料與方法綁定在同一個物件中的設計概念;資訊隱藏則是將內部實作隱藏於外部之外的原則。簡潔地說,封裝是較大的容器,而資訊隱藏則透過存取修飾詞在其中實現。
Q. Swift 中 public 和 open 有什麼差異?
兩者都能從其他模組存取,但只有open允許外部模組繼承與覆寫方法。public類別可以在外部使用,但不能被繼承。補充說明只有在開放 Framework 擴充點時才使用open即可。
存取修飾詞看似只是幾個記住就好的語法,但其實是在訓練你設計「誰可以碰這個值」。
只要親自打一遍今天的帳戶範例,應該就能豁然開朗。推薦在小型專案中先從private開始加起!

