⚠️
型の自由度とインターフェースの複雑さのトレードオフ問題について
なるべく型安全にしたいという考えは、静的型付け言語を扱う人なら誰もが持っているはずです。しかし、自由度の高い型定義を行うことで、コードが複雑化し、利便性が低下することもあります。
例えば、Entity
というインターフェースの定義についてです。Entity
はIDを持つクラスです。一番簡単な実装例は、IDの型をString
に固定する定義です。
abstract interface class Entity {
String get id;
}
この利点は、Entity
がシンプルなことですが、IDはString
型に固定されてしまいます。場合によっては、int
型やその他の型でIDを定義したいこともあるかもしれません。
その場合、ジェネリクスを利用することができます。
abstract interface class Entity<TId> {
TId get id;
}
こうすることで型の自由度が高まり、IDの型は柔軟に指定できるようになりました。一見すると、ジェネリクスを使った型定義の方が有利に見えます。しかし、ジェネリクスを使うことによるデメリットも存在します。
例えば、Entity
に対する汎用的なRepository
を実装する場合、次のようなコードになります。
abstract interface class IRepository<TId, TEntity extends Entity<TId>> {
Future<TEntity> findById(TId id);
Future<void> save(TEntity entity);
}
findById
はIDを引数に取り、Entity
を返します。この厳密な型定義を実現するために、IRepository
クラスはTId
とTEntity
のジェネリクスを要求することになります。これは、抽象的なEntity
クラスを扱うプログラミングを行う際に、頻繁に起こる問題です。
IDの型の自由度を得るために、インターフェースの複雑さを増すべきでしょうか?それとも、IDをString
型に固定し、インターフェースをシンプルに保つべきでしょうか?
これが、型の自由度とインターフェースの複雑さのトレードオフ問題です。
Discussion