Kotlinの委任パターン
1. 概要
継承よりも委任が優先される多くのユースケースがあります。 Kotlinは、これを言語レベルでサポートしています。
このチュートリアルでは、委任パターンに対するKotlinのネイティブサポートについて説明し、実際の動作を確認します。
2. 実装
まず、サードパーティのライブラリに以下の構造のコード例があると仮定します。
interface Producer {
fun produce(): String
}
class ProducerImpl : Producer {
override fun produce() = "ProducerImpl"
}
次に、既存の実装 を「by」キーワードを使用して装飾し、必要な処理を追加します。
class EnhancedProducer(private val delegate: Producer) : Producer by delegate {
override fun produce() = "${delegate.produce()} and EnhancedProducer"
}
したがって、この例では、EnhancedProducerクラスがタイプProducerのdelegateオブジェクトをカプセル化することを示しています。 また、Producer実装の機能を使用することもできます。
最後に、期待どおりに機能することを確認しましょう。
val producer = EnhancedProducer(ProducerImpl())
assertThat(producer.produce()).isEqualTo("ProducerImpl and EnhancedProducer")
3. ユースケース
次に、委任パターンの2つの一般的なユースケースを見てみましょう。
まず、委任パターンを使用して、既存の実装を使用して複数のインターフェースを実装できます。
class CompositeService : UserService by UserServiceImpl(), MessageService by MessageServiceImpl()
次に、委任を使用して、既存の実装を拡張できます。
後者は、前のセクションで行ったことです。 ただし、以下のようなより現実的な例は、既存の実装を変更できない場合に特に役立ちます。たとえば、サードパーティのライブラリコードです。
class SynchronizedProducer(private val delegate: Producer) : Producer by delegate {
private val lock = ReentrantLock()
override fun produce(): String {
lock.withLock {
return delegate.produce()
}
}
}
4. 委任は継承ではありません
ここで、デリゲートはデコレータについて何も知らないことを常に覚えておく必要があります。したがって、GoFテンプレートメソッドのようなアプローチを試してはいけません。
例を考えてみましょう:
interface Service {
val seed: Int
fun serve(action: (Int) -> Unit)
}
class ServiceImpl : Service {
override val seed = 1
override fun serve(action: (Int) -> Unit) {
action(seed)
}
}
class ServiceDecorator : Service by ServiceImpl() {
override val seed = 2
}
ここで、デリゲート( ServiceImpl )は、共通インターフェースで定義されたプロパティを使用し、それをデコレーター( ServiceDecorator )でオーバーライドします。 ただし、デリゲートの処理には影響しません。
val service = ServiceDecorator()
service.serve {
assertThat(it).isEqualTo(1)
}
最後に、Kotlinでは、インターフェースだけでなく、個別のプロパティにも委任できることに注意することが重要です。
5. 結論
このチュートリアルでは、Kotlinインターフェースの委任について、いつ使用するか、どのように構成するか、およびその注意事項について説明しました。
いつものように、この記事の完全なソースコードは、GitHubでから入手できます。