0Pricing
Kotlin Academy · レッスン

実践的なパターン

委譲でデコレーターを実装します

「実践的なパターン」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

デコレーターパターン

デコレーターは、同じインターフェースを保ったまま動作を追加するために、オブジェクトをラップします。Kotlinの by キーワードを使えば、デコレーターは簡単に作れます。すべてを転送し、拡張したい部分だけをオーバーライドします。

基底コンポーネント

まず、シンプルなデータソースから始めます。デコレーターはこれをラップします。

interface DataSource {
    fun fetch(): String
}

class NetworkSource : DataSource {
    override fun fetch() = "raw-data"
}

fun main() {
    println(NetworkSource().fetch())
}

ログ出力デコレーター

このデコレーターはラップしたソースに処理を委譲し、対象となる呼び出しの前後にログ出力を追加します。

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class LoggingSource(private val inner: DataSource) : DataSource by inner {
    override fun fetch(): String {
        println("fetching...")
        val result = inner.fetch()
        println("done")
        return result
    }
}

fun main() {
    println(LoggingSource(NetworkSource()).fetch())
}

キャッシュデコレーター

別のデコレーターは最初の結果をキャッシュするため、繰り返し呼び出しても内部のソースは呼び出されません。

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class CachingSource(private val inner: DataSource) : DataSource by inner {
    private var cached: String? = null
    override fun fetch(): String {
        return cached ?: inner.fetch().also { cached = it }
    }
}

fun main() {
    val s = CachingSource(NetworkSource())
    println(s.fetch())
    println(s.fetch()) // served from cache
}

デコレーターの積み重ね

各デコレーター自体が DataSource なので、複数を積み重ねることができます。順序が重要です。外側のデコレーターが内側のデコレーターをラップします。

interface DataSource { fun fetch(): String }
class NetworkSource : DataSource { override fun fetch() = "raw-data" }

class LoggingSource(private val inner: DataSource) : DataSource by inner {
    override fun fetch(): String { println("log"); return inner.fetch() }
}
class CachingSource(private val inner: DataSource) : DataSource by inner {
    private var c: String? = null
    override fun fetch() = c ?: inner.fetch().also { c = it }
}

fun main() {
    val s = LoggingSource(CachingSource(NetworkSource()))
    println(s.fetch())
    println(s.fetch())
}

検証デコレーター

デコレーターは入力値を検査することもできます。ここでは、実際のワーカーに委譲する前に入力を検証しています。

interface Processor { fun process(input: String): String }
class UpperProcessor : Processor { override fun process(input: String) = input.uppercase() }

class ValidatingProcessor(private val inner: Processor) : Processor by inner {
    override fun process(input: String): String {
        require(input.isNotBlank()) { "empty input" }
        return inner.process(input)
    }
}

fun main() {
    println(ValidatingProcessor(UpperProcessor()).process("hello"))
}

読み取り専用コレクションラッパー

委譲を使うと、アダプターラッパーを簡単に作成できます。ここではリストをラップし、すべてのリスト操作を転送しながら、独自の summary メソッドを追加しています。

class SummarizedList(private val inner: List<Int>) : List<Int> by inner {
    fun total() = inner.sum()
}

fun main() {
    val list = SummarizedList(listOf(1, 2, 3))
    println(list.size)
    println(list.total())
}

呼び出し回数のカウント

デコレーターを使うと、透過的にメトリクスを追跡できます。実際の実装に手を加えずに監視できるため便利です。

interface Service { fun call(): String }
class RealService : Service { override fun call() = "ok" }

class CountingService(private val inner: Service) : Service by inner {
    var calls = 0
        private set
    override fun call(): String {
        calls++
        return inner.call()
    }
}

fun main() {
    val s = CountingService(RealService())
    s.call(); s.call()
    println(s.calls)
}

ここで委譲が有利な理由

継承でデコレーターを作るには、基底クラスを open にして、各デコレーターを具象クラスに結び付ける必要があります。委譲はインターフェースだけに依存するため、デコレーターは疎結合で、組み合わせやすいまま保てます。

パターンのチェックリスト

委譲デコレーターを作成するには、次の手順に従います。

  • ラップするオブジェクトを private val として受け取る
  • by でインターフェースに委譲する
  • 拡張するメンバーだけをオーバーライドする
  • オーバーライドの中で内部オブジェクトを呼び出す

まとめて理解する

デコレーター、アダプター、プロキシは、いずれもクラス委譲から自然に導き出せます。小さなインターフェースと組み合わせることで、テストや拡張が容易な、柔軟で階層化されたシステムを構築できます。

理解度チェック

実践的な委譲パターンについての理解度を確認しましょう。

まとめ

委譲を使って実践的なデコレーターを作成しました。

  • ログ出力、キャッシュ、検証、呼び出し回数のカウントを行うラッパー
  • 各デコレーターが同じインターフェースなので、積み重ねて使用できる
  • 拡張するメンバーだけをオーバーライドし、それ以外は転送する

これでクラス委譲のコースは完了です。

よくある質問

「実践的なパターン」レッスンは無料ですか?

はい。「実践的なパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「実践的なパターン」で何を学びますか?

委譲でデコレーターを実装します ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「実践的なパターン」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. byキーワード
  2. メンバーへの委譲
  3. 委譲と継承
  4. 実践的なパターン
← Kotlin Academyに戻る