ポリモーフィズム
同じインターフェースで異なる型のオブジェクトを扱い、実行時に適切な処理を選択する仕組み
オブジェクト指向設計
ポリモーフィズムとは
ポリモーフィズム (多態性) は、同じインターフェースで異なる型のオブジェクトを扱い、実行時に適切な処理を選択する仕組みである。「何をするか」を統一し、「どうするか」を型ごとに変える。
ポリモーフィズムの種類
| 種類 | 説明 | 例 |
|---|---|---|
| サブタイプ | インターフェースを実装 | Notification を実装する Email, Slack |
| パラメトリック | ジェネリクス | Array<T>, Promise<T> |
| アドホック | 関数オーバーロード | 同名関数で異なる引数 |
TypeScript でのサブタイプポリモーフィズム
interface Notification {
send(message: string): Promise<void>;
}
class EmailNotification implements Notification {
async send(message: string) { await ses.send(message); }
}
class SlackNotification implements Notification {
async send(message: string) { await slack.post(message); }
}
// 呼び出し側は具体的な型を知らない
async function notify(notifier: Notification, message: string) {
await notifier.send(message); // 実行時に適切な send() が呼ばれる
}
await notify(new EmailNotification(), 'Hello');
await notify(new SlackNotification(), 'Hello');
Discriminated Union (TypeScript 的ポリモーフィズム)
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'rectangle': return shape.width * shape.height;
}
}
// TypeScript が網羅性をチェック
ポリモーフィズム vs if-else
// ❌ if-else: 新しい型を追加するたびに分岐を追加
function process(type: string) {
if (type === 'email') { /* ... */ }
else if (type === 'slack') { /* ... */ }
else if (type === 'sms') { /* ... */ } // 追加
}
// ✅ ポリモーフィズム: 新しい型を追加するだけ
class SmsNotification implements Notification {
async send(message: string) { /* ... */ }
}
// 既存のコードを変更せずに拡張 (開放閉鎖原則)
ポリモーフィズムのメリット
ポリモーフィズムにより、既存コードを変更せずに新しい型を追加できる (開放閉鎖原則)。テスト時にはモックに差し替えが容易で、条件分岐が型ごとのメソッドに分散されるためコードの可読性も向上する。
実践的な知識は関連書籍でも得られる。