はじめに
「抽象化が大事」とよく言われるけど、正直よく分からない。
この記事では、
「コードを追加するときに何が起きるか?」という視点で
抽象化を理解してみます。
抽象化していない世界
例えばこんなコードです👇
def print_report(report_type):
if report_type == "estimate":
print("見積書を出力します")
elif report_type == "invoice":
print("請求書を出力します")
elif report_type == "receipt":
print("領収書を出力します")
この状態で「領収書」を追加したくなったとします。
すると👇
def print_report(report_type):
if report_type == "estimate":
print("見積書を出力します")
elif report_type == "invoice":
print("請求書を出力します")
elif report_type == "receipt":
print("領収書を出力します")
elif report_type == "delivery_note": # ← ここを追加
print("納品書を出力します")
- if文を追加する
- 既存の関数を修正する
👉 すでに動いているコードを触ることになる
ここで起きる問題
- 他の処理を壊すかもしれない
- 修正のたびに影響範囲を気にする必要がある
👉 変更が怖くなる
抽象化するとどうなるか
同じことを、役割ごとに分けてみます👇
class Report:
def print(self):
raise NotImplementedError()
class Estimate(Report):
def print(self):
print("見積書を出力します")
class Invoice(Report):
def print(self):
print("請求書を出力します")
class Receipt(Report):
def print(self):
print("領収書を出力します")
この状態で「領収書」を追加すると…
class DeliveryNote(Report): # ← 新しく追加するだけ
def print(self):
print("納品書を出力します")
👉 クラスを1つ追加するだけ
- 既存コードは修正しない
- 他の処理に影響しない
👉 変更の影響範囲が限定される
テストの観点で見ると
さらに重要なのは、テストの観点です。
既存コードを修正する場合は、
どこに影響が出るか分からないため、広い範囲のテストが必要になります。
一方で、今回のように新しいコードを追加するだけで済む場合は、
影響範囲が限定されるため、テストすべき範囲も明確になります。
例えば👇
-
既存コードを修正する場合
→ 全体の動作確認が必要になる -
新しいコードを追加するだけの場合
→ 追加した部分を中心にテストすればよい
👉 結果として、テスト工数を抑えやすくなります
つまり何が違うのか
👉 既存コードを修正するか、新しいコードを追加するだけで済むかの違い
抽象化とは何か
ここでやっていることを言葉にすると👇
👉 共通部分をまとめて、違いを分けること
まとめ
- 抽象化しない → 既存コードを修正する → 変更が怖い
- 抽象化する → 新しいコードを追加する → 変更に強い
さらに、
- 影響範囲が限定される
- テスト範囲が明確になる
👉 結果として開発しやすくなる
おわりに
最初は難しく感じるかもしれませんが、
「追加するときに既存コードを触っているか?」を見るだけでも、
設計の良し悪しが分かるようになります。
この視点を持つだけでも、コードの書き方は大きく変わってきます。

