抽象化とは、細かい実装を隠し、必要な性質や操作だけを公開することです。


6.1 カプセル化

データと、それを扱う操作を一つにまとめます。

class BankAccount
{
    private int $balance = 0;

    public function deposit(int $amount): void
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException();
        }

        $this->balance += $amount;
    }
}

外部から残高を自由に変更させず、正しい操作だけを許可します。


6.2 インターフェース

実装ではなく、満たすべき契約を表します。

interface UserRepository {
  findById(id: number): Promise<User | null>;
  save(user: User): Promise<void>;
}

実装は複数存在できます。

MySQL実装
PostgreSQL実装
メモリ上のテスト実装
外部API実装

利用側は具体的な保存方法を知らなくて済みます。


6.3 継承

既存の型を基に新しい型を作ります。

Animal
  ├─ Dog
  └─ Cat

ただし、継承は実装と状態を強く結合させやすいため、現代的な設計ではコンポジションが好まれる場合があります。


6.4 コンポジション

必要な機能を別オブジェクトとして組み合わせます。

class UserService {
  constructor(
    private repository: UserRepository,
    private mailer: Mailer
  ) {}
}

「継承して機能を得る」のではなく、「必要な機能を持つオブジェクトを受け取る」設計です。


6.5 公称型と構造的型付け

公称型

型名や明示的な宣言によって互換性が決まります。

Java、C#、Rustなどが代表的です。

class UserId {
    int value;
}

構造が同じでも、別名の型とは区別されます。

構造的型付け

必要なプロパティやメソッドを持っていれば互換とみなします。

TypeScriptが代表的です。

type Named = {
  name: string;
};

const user = {
  name: "Taro",
  age: 30
};

const named: Named = user;

usernameを持つため、Namedとして扱えます。