この記事の対象読者
-
Python や JavaScript でコードは書けるが、
classを「なんとなく」使っている人 - 「カプセル化」「ポリモーフィズム」と聞いて、言葉は知ってるけど自信を持って説明できない人
- 手続き型で書いてきたが、規模が大きくなって設計が崩壊し始めている人
- 就活・転職でオブジェクト指向について聞かれて冷や汗をかいた経験がある人
この記事で得られること
- オブジェクト指向の4大原則を「会社組織」という1つの比喩で一貫して理解できる
- クラス・インスタンス・継承・カプセル化・ポリモーフィズム・抽象化の関係性が構造的に整理される
- PythonとJavaScriptの両方で動くコード例を通じて、言語を超えた本質を掴める
- 「OOPを使うべき場面」と「使わないほうがいい場面」の判断基準が身につく
- 実務でよくある設計ミスと、その回避パターンを知れる
この記事で扱わないこと
- デザインパターン(GoF)の網羅的な解説 → 別記事で扱う予定
- Java / C# / C++ など静的型付け言語固有の文法
- 関数型プログラミングとの詳細な比較
1. なぜ「今さら」オブジェクト指向なのか
正直に白状する。筆者は3年間、オブジェクト指向を「雰囲気」で書いてきた。
class は使う。__init__ も書く。継承もする。でも、なぜそうするのかを本気で考えたことがなかった。「チュートリアルがそう書いてたから」で済ませてきた。
転機になったのは、個人開発のコードベースが5,000行を超えたあたりだ。機能追加のたびに3ファイルを同時に修正しないと動かない。バグを直すと別の場所が壊れる。コードが「共依存」の人間関係みたいになっていた。
このとき初めて、「設計思想を理解せずにツールだけ使っていた」ことの代償を思い知った。
本記事では、オブジェクト指向の4大原則を 「会社組織」 という一貫した比喩で説明する。なぜ会社組織なのか。それは、オブジェクト指向が本質的に 「役割分担と責任範囲の設計」 だからだ。会社で起きる問題と、コードで起きる問題は、驚くほど構造が似ている。
この比喩を軸に、最後まで読み進めてほしい。読み終わる頃には、class を書くときの「解像度」がまるで変わっているはずだ。
2. まず手続き型の限界を体感する
オブジェクト指向を理解するには、まず「オブジェクト指向がない世界」の辛さを知るのが早い。
会社組織に例えると、これは 「全員がフリーランスで、マニュアルも部署もない会社」 だ。誰が何を担当しているかわからない。共有情報はグローバル変数という名の「全社共有ホワイトボード」に書き殴られている。
# 手続き型: 全社共有ホワイトボードに全部書く世界
employees = []
total_salary = 0
def add_employee(name, role, salary):
global total_salary
employees.append({"name": name, "role": role, "salary": salary})
total_salary += salary
def get_report():
global total_salary
for emp in employees:
if emp["role"] == "engineer":
print(f"{emp['name']}: コードレビュー完了")
elif emp["role"] == "sales":
print(f"{emp['name']}: 売上報告 提出済み")
elif emp["role"] == "designer":
print(f"{emp['name']}: デザイン納品完了")
print(f"給与総額: {total_salary}円")
add_employee("田中", "engineer", 500000)
add_employee("佐藤", "sales", 450000)
add_employee("鈴木", "designer", 480000)
get_report()
一見、動く。だが問題は「拡張」しようとした瞬間に現れる。
- 新しい職種を追加するたびに
get_report()のif-elifが伸びていく - 給与計算ロジックを変更すると、全く関係ない場所が壊れる
-
employeesというグローバル変数を誰でも触れてしまう
この「if-elif の連鎖」が手続き型の典型的な崩壊パターンだ。職種が10個になったら? 100個になったら? ...orz
次のセクションから、この問題を「会社組織の設計」で解決していく。
3. クラスとインスタンス ─ 職務記述書と実際の社員
オブジェクト指向の最も基本的な概念、クラスとインスタンス。これを会社組織で言い換えるとこうなる。
| OOP用語 | 会社組織での対応 | 役割 |
|---|---|---|
| クラス | 職務記述書(ジョブディスクリプション) | 「この職種の人は何ができて、何を持っているか」を定義 |
| インスタンス | 実際に働いている社員 | 職務記述書に基づいて採用された個別の人間 |
| 属性(プロパティ) | 社員の名前・役職・スキル | その社員固有の情報 |
| メソッド | 社員ができる業務 | 職務記述書に書かれた「やるべきこと」 |
| コンストラクタ | 入社手続き(オンボーディング) | 社員を迎え入れるときの初期設定 |
職務記述書(クラス)は「こういう人を採用します」というテンプレートであって、それ自体は人間ではない。実際に入社した田中さんや佐藤さんが、インスタンスだ。
class Employee:
"""職務記述書: 全社員に共通する基本情報と業務"""
def __init__(self, name: str, salary: int):
"""入社手続き(オンボーディング)"""
self.name = name # 社員固有の名前
self.salary = salary # 社員固有の給与
def introduce(self) -> str:
"""自己紹介する(全社員ができる業務)"""
return f"私は{self.name}です。"
def work(self) -> str:
"""業務を行う(具体的な内容はサブクラスで定義)"""
return f"{self.name}が業務を行っています。"
# 職務記述書(クラス)から実際の社員(インスタンス)を採用する
tanaka = Employee("田中", 500000)
sato = Employee("佐藤", 450000)
print(tanaka.introduce()) # 私は田中です。
print(sato.introduce()) # 私は佐藤です。
ここで重要なのは、tanaka と sato はそれぞれ独立した社員だということ。田中さんの給与を変えても、佐藤さんの給与には影響しない。データと振る舞いがセットで「個人」に紐づいている。これが手続き型との根本的な違いだ。
読者がここまでで「クラス = テンプレート、インスタンス = 実体」という関係を掴めていれば、次の原則がスムーズに入ってくる。
4. カプセル化 ─ 部署の内部事情は外に見せない
会社には「経理部」がある。経理部は会社の資金繰り、経費精算、給与計算を行う。しかし、営業部の社員が勝手に経理のスプレッドシートを開いて数字を書き換えたら大惨事になる。
これがまさにカプセル化の考え方だ。データを外部から直接触らせず、決められた窓口(メソッド)を通じてのみアクセスさせる。
class AccountingDepartment:
"""経理部: 会社の財務データを管理する"""
def __init__(self, budget: int):
self.__budget = budget # 予算(外部からの直接アクセスを禁止)
self.__expenses: list = [] # 経費リスト(同上)
# --- 公開窓口(パブリックメソッド) ---
def request_expense(self, amount: int, reason: str) -> str:
"""経費申請を受け付ける窓口"""
if amount <= 0:
return "申請却下: 金額が不正です。"
if amount > self.__budget:
return f"申請却下: 予算残高 {self.__budget}円 を超えています。"
self.__process_expense(amount, reason)
return f"承認: {reason} に {amount}円 を支出しました。残高: {self.__budget}円"
def get_budget_summary(self) -> str:
"""予算サマリを取得する窓口"""
return f"現在の予算残高: {self.__budget}円 / 支出件数: {len(self.__expenses)}件"
# --- 内部処理(プライベートメソッド) ---
def __process_expense(self, amount: int, reason: str):
"""実際の経費処理(外部からは呼べない)"""
self.__budget -= amount
self.__expenses.append({"amount": amount, "reason": reason})
accounting = AccountingDepartment(1000000)
print(accounting.request_expense(50000, "開発用サーバー"))
# 承認: 開発用サーバー に 50000円 を支出しました。残高: 950000円
print(accounting.get_budget_summary())
# 現在の予算残高: 950000円 / 支出件数: 1件
# 直接アクセスしようとすると...
# print(accounting.__budget) # AttributeError! 外部からは触れない
Pythonの __ プレフィックスは「名前マングリング」と呼ばれ、厳密な意味でのアクセス制御ではない。_AccountingDepartment__budget で強引にアクセスできてしまう。だが「触るなよ」という設計上の意思表示として機能する。完全なアクセス制御が必要なら Java や C# の private を使うべきだが、Pythonでは慣習的にこの方式が定着している。
カプセル化の本質は「情報隠蔽」ではない。「責任範囲の明確化」 だ。経理部が予算を管理する責任を持ち、その方法は経理部が決める。営業部は「経費申請」という窓口を通じて依頼するだけ。誰が何に責任を持つかが明確になることで、変更の影響範囲が限定される。
JavaScript でも同じ思想を実装できる。
class AccountingDepartment {
#budget; // プライベートフィールド(ES2022+)
#expenses = [];
constructor(budget) {
this.#budget = budget;
}
requestExpense(amount, reason) {
if (amount <= 0 || amount > this.#budget) return "申請却下";
this.#budget -= amount;
this.#expenses.push({ amount, reason });
return `承認: ${reason} に ${amount}円 支出。残高: ${this.#budget}円`;
}
}
const accounting = new AccountingDepartment(1000000);
console.log(accounting.requestExpense(50000, "開発用サーバー"));
// console.log(accounting.#budget); // SyntaxError!
JavaScriptの # プレフィックスは Pythonの __ と違い、言語レベルで完全にプライベートだ。外部からは一切アクセスできない。
カプセル化によって「部署の内部事情」が保護されたことで、コードの安全性が格段に上がった。次は、部署間の共通ルールと個別ルールの関係、つまり「継承」を見ていこう。
5. 継承 ─ 本社ルールを支社が引き継ぐ
会社には「全社共通の就業規則」がある。全社員は出退勤を記録し、自己紹介ができ、給与を受け取る。これが親クラスの役割だ。
しかし、エンジニアと営業では求められる業務が違う。エンジニアはコードを書き、営業は商談をまとめる。共通ルールを引き継ぎつつ、個別の業務を追加する ── これが継承だ。
クリックでソースコードを展開
class Employee:
"""全社共通の就業規則(親クラス)"""
def __init__(self, name: str, salary: int):
self.name = name
self.salary = salary
def introduce(self) -> str:
return f"私は{self.name}です。"
def work(self) -> str:
return f"{self.name}が業務を行っています。"
class Engineer(Employee):
"""エンジニア部門の職務記述書(子クラス)"""
def __init__(self, name: str, salary: int, language: str):
super().__init__(name, salary) # 本社ルールの入社手続きを実行
self.language = language # エンジニア固有の属性
def work(self) -> str:
"""エンジニア固有の業務(親メソッドをオーバーライド)"""
return f"{self.name}が{self.language}でコードを書いています。"
def code_review(self, target: str) -> str:
"""エンジニアだけが持つ業務"""
return f"{self.name}が{target}のコードレビューを実施中。"
class SalesPerson(Employee):
"""営業部門の職務記述書(子クラス)"""
def __init__(self, name: str, salary: int, region: str):
super().__init__(name, salary)
self.region = region
def work(self) -> str:
return f"{self.name}が{self.region}エリアで商談中です。"
def close_deal(self, client: str, amount: int) -> str:
"""営業だけが持つ業務"""
return f"{self.name}が{client}との{amount}万円の契約を締結!"
# 実際の社員を採用
tanaka = Engineer("田中", 500000, "Python")
sato = SalesPerson("佐藤", 450000, "関東")
print(tanaka.introduce()) # 私は田中です。 ← 本社ルール(継承)
print(tanaka.work()) # 田中がPythonでコードを書いています。 ← 部門固有
print(tanaka.code_review("認証モジュール")) # エンジニア専用メソッド
print(sato.introduce()) # 私は佐藤です。 ← 本社ルール(継承)
print(sato.work()) # 佐藤が関東エリアで商談中です。 ← 部門固有
print(sato.close_deal("ABC商事", 300)) # 営業専用メソッド
継承は強力だが、使いすぎると「継承の階層が深すぎて何が起きてるかわからない」という別の地獄を生む。原則として 継承の深さは2〜3階層まで に留めよう。それ以上必要な場合は、後述する「コンポジション」を検討すべきサインだ。
super().__init__() が「本社ルールの入社手続きをまず実行する」という意味だと理解できれば、継承の仕組みは腹落ちしたはずだ。次は、継承と密接に関わる「ポリモーフィズム」を見ていこう。
6. ポリモーフィズム ─ 同じ指示、違う動き
社長が全社員に「今週の業務報告をしろ」と指示を出す場面を想像してほしい。
エンジニアは「プルリクエストのマージ状況」を報告する。営業は「商談の進捗」を報告する。デザイナーは「納品したデザインの一覧」を報告する。同じ指示に対して、職種ごとに違う振る舞いをする。これがポリモーフィズムだ。
クリックでコードを展開
class Designer(Employee):
"""デザイン部門"""
def __init__(self, name: str, salary: int, tool: str):
super().__init__(name, salary)
self.tool = tool
def work(self) -> str:
return f"{self.name}が{self.tool}でデザイン作業中です。"
def weekly_report(employees: list[Employee]):
"""
社長からの指示: 全員、業務報告せよ。
社長は各社員の職種を気にしない。「work()を呼ぶ」だけ。
"""
print("=" * 50)
print("【週次業務報告】")
print("=" * 50)
for emp in employees:
print(f" {emp.work()}")
print("=" * 50)
# 全社員をまとめてリストに入れる
all_staff = [
Engineer("田中", 500000, "Python"),
SalesPerson("佐藤", 450000, "関東"),
Designer("鈴木", 480000, "Figma"),
Engineer("高橋", 520000, "TypeScript"),
]
weekly_report(all_staff)
出力:
==================================================
【週次業務報告】
==================================================
田中がPythonでコードを書いています。
佐藤が関東エリアで商談中です。
鈴木がFigmaでデザイン作業中です。
高橋がTypeScriptでコードを書いています。
==================================================
ここで注目すべきは weekly_report() 関数の中身だ。この関数は Engineer も SalesPerson も Designer も知らない。ただ「work() メソッドを持つ Employee」を受け取るだけ。新しい職種 HRPerson を追加しても、weekly_report() は一切変更不要。
これが冒頭の手続き型コードで問題だった if-elif の連鎖を根本から解消する仕組みだ。手続き型では「職種を判定して分岐」していたが、ポリモーフィズムでは 「各職種が自分の仕事を知っている」 ので分岐が不要になる。
ポリモーフィズムの恩恵は「新しい職種を追加するとき」に最も実感できる。手続き型なら if-elif に新しい条件を追加し、関連する全関数を修正する必要があった。OOPなら新しいクラスを1つ作るだけで済む。これがオープン・クローズド原則 ── 「拡張に開いて、修正に閉じている」── の実現だ。
7. 抽象化 ─ 社長は「やれ」とだけ言う
ここまでの説明で Employee クラスの work() メソッドに気づいた読者がいるかもしれない。親クラスの work() は "業務を行っています" という、なんの具体性もないメッセージを返す。現実世界で言えば、「社員は業務をします」しか書いてない職務記述書。それ、意味あるか? (;゚д゚)ポカーン
答えは「ない」。そこで登場するのが 抽象クラス だ。
抽象クラスは「契約書のテンプレート」に近い。「こういうメソッドを必ず実装しなさい」というルールだけ定めて、具体的な中身は各部門に委ねる。社長が「業務報告の仕組みは各部署で作れ、ただしフォーマットは統一しろ」と宣言するイメージだ。
from abc import ABC, abstractmethod
class Employee(ABC):
"""
抽象クラス: 社長が定めた「全社員が守るべき契約」
- 直接インスタンス化できない(社長命令書そのものは社員ではない)
- work() は各部門で必ず実装すること
"""
def __init__(self, name: str, salary: int):
self.name = name
self.salary = salary
def introduce(self) -> str:
"""具象メソッド: 全員共通の自己紹介(実装済み)"""
return f"私は{self.name}です。"
@abstractmethod
def work(self) -> str:
"""抽象メソッド: 各部門で必ず実装すること"""
pass
@abstractmethod
def monthly_report(self) -> dict:
"""抽象メソッド: 月次報告のフォーマット"""
pass
class Engineer(Employee):
"""エンジニア: 抽象メソッドを具体化"""
def __init__(self, name: str, salary: int, language: str):
super().__init__(name, salary)
self.language = language
def work(self) -> str:
return f"{self.name}が{self.language}でコードを書いています。"
def monthly_report(self) -> dict:
return {
"name": self.name,
"type": "engineering",
"commits": 142,
"reviews": 38,
}
# 抽象クラスは直接インスタンス化できない
# employee = Employee("誰か", 0) # TypeError! 抽象メソッドが未実装
work() と monthly_report() を実装せずに子クラスをインスタンス化すると TypeError が発生する。これは「契約違反」をコード実行時に自動検出する仕組みだ。チームメンバーが抽象メソッドの実装を忘れていても、テストやデプロイの段階で必ず気づける。
抽象化のポイントは、呼び出す側が「相手の内部実装を知らなくていい」 ことにある。社長は各社員に work() と monthly_report() を呼ぶだけ。その中身がPythonなのかGoなのか、Figmaなのか手描きなのかは知らないし、知る必要がない。これにより、社長(呼び出し側のコード)と社員(各クラス)の結合度が最小化される。
8. 4大原則の関係性を整理する
ここまでで4つの原則を個別に見てきた。しかし実務では、これらは独立して使うものではなく 組み合わせて初めて威力を発揮する。会社組織の比喩で全体像を整理しよう。
| 原則 | 会社組織での役割 | 解決する問題 |
|---|---|---|
| カプセル化 | 部署の内部事情を外に見せない | データの不正操作、変更の影響範囲拡大 |
| 継承 | 本社ルールを支社が引き継ぐ | コードの重複、共通ロジックの散在 |
| ポリモーフィズム | 同じ指示に職種ごとに対応 | if-elif の連鎖、新規追加時の修正範囲 |
| 抽象化 | 社長は「やれ」とだけ言う | 呼び出し側と実装側の強結合 |
4原則は「分業と自律」の設計哲学だ。 各部署が自分の仕事に責任を持ち、他の部署の仕事に干渉しない。新しい部署を作るときは本社ルールに従いつつ、自分の業務は自分で定義する。社長は各部署の細かい事情を知らなくても、統一されたインターフェースで指示を出せる。
9. 実務で踏みがちな地雷 ─ よくある設計ミスと対処法
理論を理解しても、実務では数多くの罠が待っている。筆者が踏んできた地雷と、その回避策を整理する。
| # | よくあるミス | 症状 | 対処法 |
|---|---|---|---|
| 1 | God Object(神クラス) | 1つのクラスに全機能を詰め込む | 単一責任原則に従い、1クラス1責任に分割する |
| 2 | 深すぎる継承ツリー | 4階層以上の継承で、どこで何が定義されたか追えない | コンポジション(後述)に切り替える。継承は2〜3階層まで |
| 3 | カプセル化の形骸化 | getter/setterを全属性に機械的に作る | 本当に外部公開が必要な属性だけにメソッドを用意する |
| 4 | 不適切な継承 | 「AはBの一種」でないのに継承する | is-a関係でなければ継承しない。has-a関係ならコンポジション |
| 5 | 抽象クラスの粒度ミス | 抽象メソッドが多すぎて子クラスの実装が苦痛 | インターフェース分離原則に従い、小さな抽象に分ける |
地雷1: God Object の恐怖
# BAD: 1つのクラスに全部入れてしまう「神クラス」
class Company:
def hire(self): ...
def fire(self): ...
def calculate_salary(self): ...
def send_invoice(self): ...
def design_logo(self): ...
def deploy_server(self): ...
def order_lunch(self): ... # 草
会社組織で言えば、社長が採用も経理も営業もデザインもサーバー運用も弁当の手配も全部やっている状態 だ。当然、破綻する。
# GOOD: 責任を分割する
class HRDepartment:
def hire(self): ...
def fire(self): ...
class AccountingDepartment:
def calculate_salary(self): ...
def send_invoice(self): ...
class EngineeringDepartment:
def deploy_server(self): ...
地雷4: 継承 vs コンポジション ─ 「is-a」か「has-a」か
継承を使うべきか、コンポジション(部品として持つ)を使うべきか。判断基準はシンプルだ。
-
is-a関係(〜は〜の一種) → 継承
- 「エンジニアは社員の一種」 →
class Engineer(Employee)✅
- 「エンジニアは社員の一種」 →
-
has-a関係(〜は〜を持っている) → コンポジション
- 「部署はプリンターを持っている」 →
self.printer = Printer()✅
- 「部署はプリンターを持っている」 →
# BAD: プリンターは社員の一種ではない
class Printer(Employee): # これはおかしい
pass
# GOOD: 部署がプリンターを「持っている」
class Department:
def __init__(self, name: str):
self.name = name
self.printer = Printer() # コンポジション
def print_report(self, content: str):
self.printer.execute(content)
迷ったらコンポジションを選べ。継承は強力だが結合度が高い。コンポジションは柔軟で、後から部品を差し替えやすい。「継承よりコンポジションを優先せよ」はGoFデザインパターンの時代から語り継がれる格言だ。
10. ユースケース別 ─ OOPはいつ使うべきか
オブジェクト指向は万能ではない。使うべき場面と、使わないほうがいい場面がある。
ユースケース1: Webアプリのユーザー管理
ユーザーには「一般ユーザー」「管理者」「ゲスト」など複数の種類があり、それぞれ権限が異なる。まさに「職種ごとに権限が違う会社組織」と同じ構造だ。
class User(ABC):
def __init__(self, name: str, email: str):
self.name = name
self.email = email
@abstractmethod
def get_permissions(self) -> list[str]:
pass
class AdminUser(User):
def get_permissions(self) -> list[str]:
return ["read", "write", "delete", "manage_users"]
class GuestUser(User):
def get_permissions(self) -> list[str]:
return ["read"]
ユースケース2: データ処理パイプライン
ETL処理など、複数の処理ステップを統一的に扱いたい場合。各ステップが「部署」に相当し、共通のインターフェースで処理を流す。
class PipelineStep(ABC):
@abstractmethod
def execute(self, data: dict) -> dict:
pass
class ValidateStep(PipelineStep):
def execute(self, data: dict) -> dict:
if "email" not in data:
raise ValueError("email is required")
return data
class TransformStep(PipelineStep):
def execute(self, data: dict) -> dict:
data["email"] = data["email"].lower()
return data
class Pipeline:
def __init__(self, steps: list[PipelineStep]):
self.steps = steps
def run(self, data: dict) -> dict:
for step in self.steps:
data = step.execute(data) # ポリモーフィズム!
return data
ユースケース3: OOPを使わないほうがいい場面
| 場面 | 理由 | 代替手法 |
|---|---|---|
| 単純なスクリプト(50行以下) | クラス定義のオーバーヘッドが無駄 | 関数だけで十分 |
| データの変換・フィルタリング | 状態を持たない処理が中心 | 関数型アプローチ(map/filter/reduce) |
| 数値計算・科学計算 | 行列演算等はOOPと相性が悪い | NumPy / Pandas の関数的API |
| プロトタイプ段階 | 設計が固まっていない段階で構造を作り込むのは時間の無駄 | まず動くものを関数で書き、構造が見えてからクラスに整理 |
11. 学習ロードマップ ─ 次に何を学ぶべきか
オブジェクト指向の基礎を理解した読者が、次のレベルに進むためのロードマップを示す。
| レベル | 学ぶべきこと | 推奨リソース |
|---|---|---|
| Lv.1 基礎 | 本記事の内容。クラス/インスタンス/4大原則 | 本記事 + 公式ドキュメント |
| Lv.2 設計原則 | SOLID原則(特に単一責任・開放閉鎖・依存性逆転) | 「Clean Code」Robert C. Martin |
| Lv.3 デザインパターン | Strategy / Observer / Factory をまず押さえる | 「Head Firstデザインパターン」 |
| Lv.4 アーキテクチャ | クリーンアーキテクチャ、ドメイン駆動設計 | 「Clean Architecture」Robert C. Martin |
Lv.2 → Lv.3 の壁が最も高い。デザインパターンを「暗記」しようとすると挫折する。まず実務で「このコード、拡張しづらいな」という痛みを感じてから学ぶと、パターンの存在意義が腹落ちする。
12. 環境別サンプル ─ 3つの実行環境で動かす
本記事のコードを実際に動かすための環境設定を示す。
クリックで環境セットアップを展開
ローカル環境(推奨):
# requirements.txt は不要(標準ライブラリのみ使用)
# Python 3.10+ を推奨(型ヒント構文の対応のため)
# 実行方法
# python oop_example.py
Docker環境:
# docker-compose.yml
version: "3.8"
services:
oop-demo:
image: python:3.12-slim
volumes:
- ./src:/app
working_dir: /app
command: python oop_example.py
オンライン環境:
# Google Colab / Replit で即実行可能
# 追加インストール不要
# Python 3.10+ が利用可能な環境であればどこでもOK
まとめ
3年間「雰囲気」でオブジェクト指向を使ってきた自分に、最も伝えたかったのはこの一言だ。
オブジェクト指向は「コードの書き方」ではなく「責任の分け方」だ。
会社組織と同じように、誰が何に責任を持ち、どの窓口を通じてやり取りするかを設計する。それがクラス設計の本質であり、4大原則はすべてこの思想の具体化にすぎない。
- カプセル化 → 部署の内部事情は窓口を通じてのみ公開する
- 継承 → 本社ルールを引き継ぎつつ、部門固有の業務を追加する
- ポリモーフィズム → 同じ指示を出せば、各部署が自律的に動く
- 抽象化 → 社長は「何をやれ」とだけ言い、「どうやるか」は各部署に任せる
これを意識してコードを書くだけで、「機能追加のたびに3ファイル同時修正」という地獄からは確実に解放される。
もし本記事を読んで「これ、自分のコードにも当てはまるな」と思った箇所があったなら、まずはそこから1クラスだけリファクタリングしてみてほしい。小さな一歩が、設計力を根本から変える。
参考文献
- Python公式ドキュメント - クラス
- MDN Web Docs - JavaScript クラス
- Robert C. Martin「Clean Code」
- Erich Gamma et al.「デザインパターン」
この記事が役に立ったと感じたら、いいねとストックをいただけると励みになります。