PHPの「継承地獄」をHackの型システムで浄化する:疎結合な設計への再構築
PHPの現場でよく見る光景がある。巨大な抽象クラス、深すぎる継承階層、そして「何ができるか」ではなく「何であるか」を強制するクラスツリー。これらはエンジニアの認知負荷を増大させ、テストを困難にし、最終的に修正不可能な技術的負債となる。
Hackの型システムは、単なる「型チェック」のための道具ではない。設計の意図をコンパイル時に強制し、実行時の不安定さを排除するための強力な制約エンジンだ。
今回は、PHPの乱雑な継承関係をHackのインターフェースとトレイト、そして`shape`や`type alias`を駆使して、どのように「疎結合で堅牢な設計」へ昇華させるかを伝授する。
—
1. 継承地獄の正体:PHP的アプローチの限界
PHPでは、コードの再利用を目的に「何でも入りの親クラス」を作りがちだ。だが、継承は「is-a(〜である)」関係を固定する。一度継承すると、そのクラスは親の制約から逃れられない。
Hackでは、「is-a」をインターフェースで定義し、「has-a(振る舞い)」をトレイトで注入する。これが基本原則だ。
アンチパターン:継承による機能の詰め込み
// 悪夢の始まり:巨大な抽象クラス
abstract class BaseService {
protected function log(): void { / … / }
protected function db(): void { / … / }
protected function sendEmail(): void { / … / }
}
// これを継承すると、メールを送らないクラスにもsendEmailメソッドが見えてしまう
—
2. Hack的再設計:インターフェースによる契約とトレイトによる合成
疎結合な設計を実現するには、インターフェースで契約を最小化し、トレイトで機能を構成する。
美しい設計例:コンポーネント指向のコード
特定の外部API(非同期処理)を叩くサービスを想定する。
namespace App\Service;
// 1. インターフェースは「振る舞い」を最小限に定義
interface IAsyncClient {
public function callAsync(string $endpoint): Awaitable
}
// 2. トレイトで実装を注入(再利用可能なパーツ化)
trait TLogger {
protected function log(string $msg): void {
\HH\Lib\print_stderr($msg . “\n”);
}
}
// 3. 実装クラスはインターフェースを実装し、必要なトレイトのみを持つ
final class UserApiClient implements IAsyncClient {
use TLogger;
public async function callAsync(string $endpoint): Awaitable
$this->log(“Requesting: ” . $endpoint);
// HHVMの非同期I/Oを活用
return await \HH\Asio\curl_exec($endpoint);
}
}
なぜこれが良いのか:
- 型安全性: `IAsyncClient`を要求する関数は、`UserApiClient`の実装詳細を知る必要がない。
- テスト容易性: インターフェースをモック化するだけで、テスト時に実際のI/Oを遮断できる。
- HHVMの恩恵: HHVMはインターフェース呼び出しの最適化に優れている。型が確定していれば、JITコンパイラは驚異的な速度でインライン展開を行う。
—
3. 型システムによる「疎結合」の担保:Shapeの活用
クラスの継承を避けるもう一つの強力な武器が `shape` だ。データの構造をクラスではなく「形」として定義することで、不要な継承を排除できる。
// 継承地獄を回避するデータ構造定義
type TApiConfig = shape(
‘timeout’ => int,
‘retries’ => int,
‘endpoint’ => string,
);
final class ApiExecutor {
public function __construct(private TApiConfig $config) {}
public function execute(): void {
// 実行時にキーが存在するか不安になる必要はない
// 型チェッカーがコンパイル時にすべてを保証する
$timeout = $this->config[‘timeout’];
}
}
この手法を使えば、「設定を保持するためだけの継承クラス」を作る必要は一切なくなる。
—
4. パフォーマンスを意識したコンポーネント設計
HHVMのアーキテクチャにおいて、トレイトを多用してもパフォーマンス上のペナルティはほぼない。しかし、型チェックの複雑化には注意が必要だ。
- インターフェースの肥大化を避ける: インターフェースを細分化し、`interface A { … }` と `interface B { … }` に分け、`interface Combined extends A, B {}` のように合成する。HHVMは複雑な階層よりも、フラットな契約を好む。
- `Awaitable`の伝播: 非同期API連携において、戻り値を安易に`mixed`にしないこと。必ず`Awaitable
`として型を明示せよ。型推論をエンジンに任せすぎると、JITコンパイル時の最適化の機会を逃すことになる。
—
結びに:なぜ我々はHackを使うのか
PHPのコードをHackへ移行する際、単なる「型付け」で終わらせてはいけない。それは「設計を見直す絶好のチャンス」だ。
- `extends`を減らす。
- `implements`で契約を定義する。
- `use`で振る舞いを合成する。
- `shape`でデータ構造を宣言する。
これらを実践するだけで、あなたのシステムは「予測可能な、エンジニアが触るのを恐れないコード」へと変貌する。HHVMという世界最強のエンジンを搭載しているのだから、それに相応しいエレガントな設計を構築しよう。
それが、Hackを掌握するということだ。