1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【デザインパターン】 デコレータパターン解説(Flutter / Android 実例付き)

Last updated at Posted at 2025-08-28

1. パターンの意図

デコレータ(Decorator)パターン は、
既存のオブジェクトに動的に機能を追加できるようにする デザインパターンです。

解決する問題

  • 継承で機能を拡張するとクラス数が爆発する
  • オブジェクトごとに「必要な機能だけ」柔軟に追加したい
  • 動的に入れ替え可能な機能拡張が欲しい

📌 ポイント

  • 包む(wrap)」ことで機能を拡張

  • 組み合わせ自由に追加可能

  • 実行時に差し込める柔軟性がある


2. UML 図

  • Component:共通インターフェース

  • ConcreteComponent:元の機能

  • Decorator:Component を保持し、機能を拡張

  • ConcreteDecorator:実際の拡張処理


3. Flutter / Dart 実装例

3.1 Component

abstract class Coffee {
  String getDescription();
  double cost();
}

3.2 ConcreteComponent

class SimpleCoffee implements Coffee {
  @override
  String getDescription() => "Simple Coffee";

  @override
  double cost() => 2.0;
}

3.3 Decorator

abstract class CoffeeDecorator implements Coffee {
  final Coffee coffee;
  CoffeeDecorator(this.coffee);

  @override
  String getDescription() => coffee.getDescription();

  @override
  double cost() => coffee.cost();
}

3.4 ConcreteDecorator

class MilkDecorator extends CoffeeDecorator {
  MilkDecorator(Coffee coffee) : super(coffee);

  @override
  String getDescription() => "${super.getDescription()}, Milk";

  @override
  double cost() => super.cost() + 0.5;
}

class SugarDecorator extends CoffeeDecorator {
  SugarDecorator(Coffee coffee) : super(coffee);

  @override
  String getDescription() => "${super.getDescription()}, Sugar";

  @override
  double cost() => super.cost() + 0.2;
}

3.5 利用例

void main() {
  Coffee coffee = SimpleCoffee();
  print("${coffee.getDescription()} : \$${coffee.cost()}");

  coffee = MilkDecorator(coffee);
  coffee = SugarDecorator(coffee);

  print("${coffee.getDescription()} : \$${coffee.cost()}");
}

出力:

Simple Coffee : $2.0
Simple Coffee, Milk, Sugar : $2.7

4. Android / Kotlin 実装例

4.1 Component

interface Coffee {
    fun getDescription(): String
    fun cost(): Double
}

4.2 ConcreteComponent

class SimpleCoffee : Coffee {
    override fun getDescription() = "Simple Coffee"
    override fun cost() = 2.0
}

4.3 Decorator

abstract class CoffeeDecorator(private val coffee: Coffee) : Coffee {
    override fun getDescription(): String = coffee.getDescription()
    override fun cost(): Double = coffee.cost()
}

4.4 ConcreteDecorator

class MilkDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun getDescription() = super.getDescription() + ", Milk"
    override fun cost() = super.cost() + 0.5
}

class SugarDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun getDescription() = super.getDescription() + ", Sugar"
    override fun cost() = super.cost() + 0.2
}

4.5 利用例

fun main() {
    var coffee: Coffee = SimpleCoffee()
    println("${coffee.getDescription()} : $${coffee.cost()}")

    coffee = MilkDecorator(coffee)
    coffee = SugarDecorator(coffee)

    println("${coffee.getDescription()} : $${coffee.cost()}")
}

5. メリット / デメリット

メリット

  • 継承を使わずに動的に機能追加できる
  • 必要な組み合わせだけで拡張可能
  • 責務を小さなデコレーションに分割できる

デメリット

  • ラップが多段になるとデバッグが複雑になる
  • 設計を誤るとどこで何を追加しているか見えにくくなる

6. 実務ユースケース

Flutter

  • Widget の修飾Padding, Container, DecoratedBox など)
  • Stream/Provider のラップ(追加処理を注入)
  • UI に機能を後付けするカスタムラッパ

Android (Kotlin)

  • Java I/O APIBufferedInputStream, DataInputStream → InputStream をデコレート)
  • RecyclerView.ItemDecoration(表示の装飾)
  • OkHttp Interceptor(リクエスト/レスポンスをデコレート)

7. 実装上の注意点

  • 「機能追加」と「状態変更」を混同しないこと
  • デコレータは 本体を包んで責務を追加する のみに絞ると明快
  • デバッグしやすいように ログ出力や階層確認を仕込むと安心

8. どんなときに使う?

  • 継承でクラス爆発しそうなとき
  • 動的に「必要な機能だけ追加」したいとき
  • UI のレイヤー修飾、I/O ラップ、Interceptor のように 責務を差し込む場面

まとめ

  • デコレータパターンは「オブジェクトを包んで機能を動的に追加する」
  • Flutter では Widget、Android では Java I/O や RecyclerView に代表例
  • 継承ではなく合成で拡張する柔軟性を得られる
  • ただし多段ラップは追跡が難しくなるため、設計の見通しが大事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?