オブジェクト指向とは何か|TicketCraftを例に
配属初日、あなたは先輩からこう言われます。
「うちの社内ツール『TicketCraft』、そろそろ限界だから触ってみて」
TicketCraftは、社内のバグ報告・機能要望・問い合わせを一元管理するチケット管理ツールです。5年前に誰かが書いたコードが、継ぎ足し継ぎ足しで動いています。ソースを開いてみると、こんな一文が目に飛び込んできます。
public class Ticket {
public String id;
public String title;
public String type; // "bug" か "feature" か "inquiry" の文字列で管理…
public String status; // "open", "wip", "done" など、表記がバラバラ
public String assignee;
}
チケットの種類ごとの処理は、あちこちに散らばったif (ticket.type.equals("bug")) { ... } else if (ticket.type.equals("feature")) { ... }という分岐で表現されています。新しい種類のチケットを1つ増やすだけで、10箇所以上のif文に手を入れる羽目になる。先輩が「限界」と言った理由が分かってきました。
この連載では、このTicketCraftをオブジェクト指向プログラミング(OOP: Object-Oriented Programming) の考え方で、少しずつ健全な設計に直していきます。今回はその第一歩として、「OOPとは何か」「なぜ今のTicketCraftはつらいのか」を掴みます。
OOPは「会社の部署制」だと考える
TicketCraftの今のコードは、たとえるなら 「全社員が同じ1つの共有ロッカーに直接手を突っ込んで書類を書き換えている」 状態です。誰でもどのデータでも自由に読み書きできてしまうため、書式の統一が取れず、誰かが変な値("wip"なのか"WIP"なのか"in_progress"なのか)を書き込むと、他の処理が静かに壊れます。
オブジェクト指向は、これを 「会社に部署を作る」 という発想で解決します。
- 各部署(=オブジェクト)は、自分の管轄のデータ(書類)を自分の引き出しにしまい、他の部署が勝手に開けられないようにする
- 他の部署とやり取りしたいときは、決められた窓口(=メソッド)を通す
- 「経理部にはこの書式で申請してください」というルールがあるように、オブジェクトも「このメソッドにはこの形式のデータを渡してください」というルールを持つ
これにより、「どこかの部署がおかしなデータを作ってしまい、全社が混乱する」という事故を防げます。
🧓 ベテランの現場コラム 「オブジェクト指向は難しい概念」と身構える人が多いですが、実際にはプログラミング特有の話というより、「責任の範囲をはっきりさせる」 という、仕事の進め方そのものの話です。「このデータは誰が管理していて、誰が変更してよいのか」を最初に決めておく癖は、コードだけでなくチームの仕事の進め方にもそのまま応用が効きます。
OOPの4大原則:連載全体の地図
OOPは主に次の4つの原則で構成されます。この連載では、それぞれを1本ずつの記事にして、TicketCraftを一緒に育てながら理解していきます。
| 原則 | 一言で言うと | この連載での担当回 |
|---|---|---|
| カプセル化(Encapsulation) | データを隠し、決められた窓口だけで操作させる | 第2回 |
| 継承(Inheritance) | 共通の土台を引き継ぎ、差分だけを追加する | 第3回 |
| ポリモーフィズム(Polymorphism) | 同じ指示で、相手ごとに適切な動作をさせる | 第4回 |
| 抽象化(Abstraction) | 本質的な操作だけを見せ、実装の詳細を隠す | 第5回 |
先ほどのTicketCraftのif (ticket.type.equals("bug")) { ... }という分岐だらけのコードは、この4原則のいずれも使えていない状態です。逆に言えば、この4つを順番に適用していくだけで、この連載の最後には見違えるほど整理されたコードになります。
目指すゴール(連載完走時のイメージ)
// 連載を通して、最終的にこのような形を目指す
abstract class Ticket {
// 共通のフィールド・メソッドはここに集約(カプセル化・抽象化)
}
class BugTicket extends Ticket { /* Ticketを継承し、差分だけ追加 */ }
class FeatureTicket extends Ticket { /* 同上 */ }
// 呼び出し側は型を意識せず、同じ書き方で扱える(ポリモーフィズム)
for (Ticket ticket : tickets) {
System.out.println(ticket.summary());
}
「型ごとのif分岐」が、呼び出し側から綺麗に消えているのが分かるはずです。
🧓 ベテランの現場コラム 新人のうちは「4原則を全部きっちり理解してからコードを書こう」と気負いがちですが、実際の現場では 「今のコードのどこが一番痛いか」に気づいた原則から手をつけるのが普通です。TicketCraftの場合、まず一番痛いのは「データが誰からでも書き換えられる」ことなので、次回のカプセル化から着手するのは理にかなった順番です。
OOPの利点(今つらい理由の裏返し)
- モジュール化: 各オブジェクトが独立した単位になるため、影響範囲を絞って変更できる
- 再利用性: 継承やインターフェースにより、似た処理を毎回書き直さずに済む
- 保守性: カプセル化・抽象化により、内部実装を変えても呼び出し側に影響が及びにくい
- 拡張性: ポリモーフィズムにより、新しい種類を追加しても既存コードの分岐を増やさずに済む
今のTicketCraftがつらいのは、裏を返せばこれら4つの利点をまったく享受できていないからです。
挫折ポイントTOP3と対処法
1. 「4原則を全部覚えてから書こう」として手が止まる
完璧な理解を待たず、まず1つの原則(カプセル化)から手を動かして構いません。原則同士は連載を通して自然につながっていきます。
2. 「クラスを作ること」自体が目的化してしまう
クラスやオブジェクトはあくまで手段です。「このデータと処理は誰が責任を持つべきか」という問いに答えるためにクラスを使う、という順番を忘れないようにしましょう。
3. 手続き型のコードを全否定してしまう
小さいスクリプトや使い捨てのツールでは、手続き型のシンプルな書き方の方が向いていることもあります。TicketCraftのように「長期間、複数人で保守する」規模のコードで威力を発揮する、という前提を押さえておきましょう。
🧓 ベテランの現場コラム ベテランでも、すべてのコードをOOPでガチガチに設計するわけではありません。ちょっとした検証スクリプトまでクラス設計に凝る必要はなく、「これは育てていくシステムか、使い捨てのスクリプトか」を最初に見極める判断力の方が、実は原則そのものの暗記より重要だったりします。
この先、何を学べばいいか
次回は、TicketCraftの最も痛い部分である「フィールドが全部public」問題に着手します。カプセル化によって、データを安全に守る方法を学びます。
まとめ
- 今のTicketCraftは「全員が同じロッカーに直接手を突っ込める」状態で、これがバグの温床になっている
- OOPは「部署制」のように、データと処理の責任範囲を明確にする考え方
- 4大原則(カプセル化・継承・ポリモーフィズム・抽象化)は、それぞれ1本ずつの記事でTicketCraftに適用していく
- 原則の暗記より、「今のコードの何が痛いか」から手をつける方が実践的
🎓 理解度テスト
学んだ内容を確認しましょう。全5問。