章: 第1章: オブジェクト指向の深化
割引ロジックを追加するたびにクラスを直接書き換えていませんか?
修正のたびに既存テストが壊れ、バグが混入するリスクが高まります。
SOLID原則は理論として読むだけでは意味がありません。実際のクラス設計で適用して初めて力を発揮します。
今回は特にO・D原則を手を動かして確かめていきます。「インターフェイスに変えるだけで、設計がどう変わるか」を実感してください。
O原則:開放閉鎖の原則 — 拡張に開き、修正に閉じる
<?php
// NG: 新しい割引を追加するたびにこのクラスを変更しなければならない
class PriceCalculator {
public function calculate(int $price, string $type): int {
if ($type === 'member') return (int)($price * 0.9);
if ($type === 'sale') return (int)($price * 0.8);
return $price;
}
}
// OK: インターフェイスで拡張に開く
interface Discount {
public function apply(int $price): int;
}
class MemberDiscount implements Discount {
public function apply(int $price): int { return (int)($price * 0.9); }
}
class SaleDiscount implements Discount {
public function apply(int $price): int { return (int)($price * 0.8); }
}
O原則:直接分岐 vs インターフェイス化の比較
| 観点 | NG(直接分岐) | OK(インターフェイス化) |
| 新割引の追加 | 既存クラスを変更 → 既存テストが再検証必要 | 新クラスを追加するだけ |
| テストの安定性 | 変更のたびに全分岐を再テスト | 既存クラスのテストは影響なし |
| 責務の明確さ | 割引の種類が増えるほど肥大化 | 割引ごとに1クラス |
| 読みやすさ | if文が増え続ける | クラス名が意図を表す |
チェックポイント: 「この関数にif文が増えていく」と気づいたら、Oの原則を使うサインです。インターフェイスへの切り出しを検討しましょう。
D原則:依存性逆転の原則 — 具体ではなく抽象に依存する
<?php
// NG: 具体クラスに直接依存 → MySQLからPostgreSQLに変えるとコード修正が必要
class OrderService {
private MysqlOrderRepository $repo;
public function __construct() {
$this->repo = new MysqlOrderRepository();
}
}
// OK: インターフェイスに依存 → 実装の差し替えが容易
interface OrderRepository {
public function findById(int $id): Order;
}
class OrderService {
public function __construct(private OrderRepository $repo) {}
}
チェックポイント: コンストラクタで
new 具体クラス()を書いていたら依存性逆転の原則を見直す機会です。
実践での適用コスト比較
| 適用タイミング | コスト | メリット |
| 設計段階から適用 | 小(クラス分離の考慮のみ) | テスト・拡張コストが低い状態を維持 |
| 小規模後に適用 | 中(リファクタリング要) | 早期なら影響範囲が小さい |
| 大規模後に適用 | 大(既存コードへの影響大) | 修正箇所が広範囲に及ぶ |
チェックポイント: SOLID違反のリファクタリングは後になるほどコストが増します。「このif文、今は3種類だけど増えそうか?」という先読みが設計の鍵です。
なぜ実践で学ぶことが大切か
SOLID原則は読んだだけでは「なんとなくわかった」で終わります。しかし実際のコードに適用する瞬間、設計の意思決定が具体的になります。
- 「この if文、インターフェイスに変えられる?」と考えるようになる
- 「テストが書きにくい」という感覚が設計の問題として捉えられる
- リファクタリングの方向性が言語化できるようになる
まとめ & 次のステップ
- SOLIDは実際のクラス設計で適用して初めて意味を持つ
- O(開放閉鎖)はインターフェイスを使って「追加に開き、修正に閉じる」を実現する
- D(依存性逆転)はコンストラクタで具体クラスを
newしないことから始まる - SOLID違反のリファクタリングは早いほどコストが低い
次回は依存性の注入(DI)を学びます。「インターフェイスに依存する」設計を実現するための具体的な手法です。