抽象化とカプセル化、いつも混同していませんか?この記事で整理します
オブジェクト指向(OOP)を学んでいると、必ずぶつかる壁があります。
それが抽象化とカプセル化です。
この2つはほとんど同じ意味に聞こえがちです。面接で「抽象化とカプセル化の違いは何ですか?」と聞かれると、曖昧に答えやすいテーマでもあります。
まず結論から説明します。
抽象化は「何を見せるか」を決める設計上の観点で、カプセル化は「どう隠すか」を決める実装上の観点です。
この一文を押さえれば半分は理解できています。残りは例でしっかり定着させましょう。
抽象化とは?
抽象化(Abstraction)とは、複雑な内部を隠し、本質的に必要な部分だけを取り出して見せることです。
車を考えてみてください。運転するとき、エンジンが燃料をどう燃焼させるかを知る必要はありません。
ハンドル、ペダル、ギア。このインターフェースが分かれば運転できます。
つまり、運転者に何を公開するかを考えるのが抽象化です。
コードでは次のように表現できます。
protocol Coffee {
func brew()
}
struct Americano: Coffee {
func brew() {
print("アメリカーノを抽出します")
}
}
let menu: Coffee = Americano()
menu.brew()
// 出力:アメリカーノを抽出します
利用側が知るのは本質であるbrew()だけです。豆をどう挽くか、水を何度に設定するかは関係ありません。
これが抽象化です。
カプセル化とは?
カプセル化(Encapsulation)とは、データとそれを扱う機能を1つにまとめ、外部からの直接アクセスを防ぐことです。
核心は隠蔽と保護です。
銀行口座を思い浮かべてください。外部から残高を自由に変更できたら大変ですよね。
そこで残高を隠し、入金や出金など決められた経路からだけ変更できるようにします。
class Account {
private var balance = 0
func deposit(_ amount: Int) {
guard amount > 0 else { return }
balance += amount
}
func getBalance() -> Int {
return balance
}
}
let acc = Account()
acc.deposit(1000)
print(acc.getBalance())
// 出力: 1000
balanceをprivateで防ぎました。外部からは、決められた窓口であるdepositを通してのみ値を変更できます。
このように内部を隠し、アクセスを制御するのがカプセル化です。
では、決定的な違いは?
まとめると次のとおりです。
| 区分 | 抽象化 | カプセル化 |
|---|---|---|
| 目的 | 複雑さを隠し、本質だけを公開 | データを隠して保護 |
| 観点 | 設計の観点(何を) | 実装の観点(どう) |
| 問い | 「何を見せるか?」 | 「どう隠すか?」 |
| 手段 | インターフェース、抽象クラス | アクセス修飾子(privateなど) |
最も混同しやすい点を押さえましょう。
どちらも何かを隠すため、混同しやすいのです。
しかし、隠す対象が異なります。
抽象化は複雑な処理(ロジック)を隠して利用を単純にします。一方、カプセル化はデータ(状態)を隠して安全に保護します。
いつ、どちらを意識すべき?
実務では別々に使うのではなく、組み合わせて使います。ただし焦点が異なります。
- 新機能のインターフェースを設計するとき → 抽象化に集中
- クラス内部の状態の整合性を守るとき → カプセル化に集中
- 協業で他者に公開するAPIを考えるとき → 抽象化
- バグなく値を安全に管理するとき → カプセル化
面接ではこう聞かれます
Q. 抽象化とカプセル化の違いを説明してください。
ポイントは観点の違いです。抽象化は不要な詳細を隠して本質だけを示す設計概念で、カプセル化はデータを隠してアクセスを制限する実装概念だと答えれば十分です。抽象化はインターフェースで、カプセル化はアクセス修飾子で実現すると付け加えるとよいでしょう。
Q. なぜカプセル化が必要なのですか?
オブジェクトの状態を外部から直接変更すると、データの整合性が崩れやすくなります。決められたメソッドからだけ状態を変更できるようにすれば、検証ロジックを一か所に集約でき、保守とデバッグが容易になると説明できます。
この2つは正反対ではなく、対になる概念です。今日の例で理解できたなら、次にコードを書くとき「今、何を見せて何を隠しているか?」と一度だけ考えてみてください。OOPがずっと明確になります。

