Decoratorパターンについて調べたのでFlutterでサンプルを作ってみた

# Decoratorパターンについて軽く

とあるクラスを拡張する場合、通常は継承して拡張部分を作成すれば良いが、
継承パターンが複数あり、またそれぞれを複数組み合わせて使用できる、
などの時は、継承だと組み合わせパターン分のサブクラスを作らなくてはならず、効率が悪い。

そのような時に継承ではなく拡張できるパターンがDecoratorパターン。

同じ抽象クラスを元とした拡張クラスを用意し、
まずは一番基本となるオブジェクトを作成する。
拡張時には、この基本オブジェクトを渡し、
マトリョーシカのように、拡張部分を重ね合わせたオブジェクトを作成する。
別の拡張をするときは、拡張済みオブジェクトを渡し、さらに拡張が重ね合わさったものを作成する。
といったように、自由に拡張していくことが可能。

# サンプルとして何を作ったか

「ノーマル」「文字を大きく」「文字を赤く」「両方」の4つのボタンを配置し、各拡張をDecoratorパターンで実装する

# クラス図

```mermaid
classDiagram
  class OutputText {
    <<abstract>>
    +getColor()
    +getFontSize()
    +getText()
  }
  class NormalText {
    +getColor()
    +getFontSize()
    +getText()
  }
  class Decorator {
    <<abstract>>
    +getColor()
    +getFontSize()
    +getText()
  }
  class LargeText {
    +getColor()
    +getFontSize()
    +getText()
  }
  class RedText {
    +getColor()
    +getFontSize()
    +getText()
  }
  class MyApp {
  }
  class MyHomePage {
  }
  class _MyHomePageState {
  }

  OutputText <|.. NormalText
  OutputText <|-- Decorator
  Decorator <|-- LargeText
  Decorator <|-- RedText

  MyHomePage <-- MyApp
  _MyHomePageState <-- MyHomePage
  NormalText <-- _MyHomePageState
  LargeText <-- _MyHomePageState
  RedText <-- _MyHomePageState
```

# コード

[GitHub](https://github.com/kenta4327/flutter_decorator) に完全版があります。

```dart:output_text.dart
import 'dart:ui';

abstract class OutputText {
  Color getColor();
  double getFontSize();
  String getText();
}
```

```dart:normal_text.dart
import 'dart:ui';

import 'package:flutter_decorator/utils/output_text.dart';

class NormalText implements OutputText {
  @override
  Color getColor() {
    return const Color(0xFF000000);
  }

  @override
  double getFontSize() {
    return 20;
  }

  @override
  String getText() {
    return '文字';
  }
}
```

```dart:decorator.dart
import 'package:flutter_decorator/utils/output_text.dart';

abstract class Decorator extends OutputText {
  OutputText outputText;

  Decorator(this.outputText);
}
```

```dart:large_text.dart
import 'dart:ui';

import 'package:flutter_decorator/utils/decorator.dart';
import 'package:flutter_decorator/utils/output_text.dart';

class LargeText extends Decorator {

  LargeText(OutputText outputText) : super(outputText);

  @override
  Color getColor() {
    return outputText.getColor();
  }

  @override
  double getFontSize() {
    final fontSize = outputText.getFontSize();
    return fontSize + 20;
  }

  @override
  String getText() {
    final text = outputText.getText();
    return '大きい$text';
  }
}
```

```dart:red_text.dart
import 'dart:ui';

import 'package:flutter_decorator/utils/decorator.dart';
import 'package:flutter_decorator/utils/output_text.dart';

class RedText extends Decorator {

  RedText(OutputText outputText) : super(outputText);

  @override
  Color getColor() {
    return const Color(0xFFC62828);
  }

  @override
  double getFontSize() {
    return outputText.getFontSize();
  }

  @override
  String getText() {
    final text = outputText.getText();
    return '赤い$text';
  }
}
```

```dart:main.dart
  :
  : // 割愛
  :
class _MyHomePageState extends State<MyHomePage> {
  final _textController = TextEditingController();
  String _outputText = '';
  double _outputFontSize = 10;
  Color _outputColor = const Color(0xFF000000);
  OutputText? _textNormal;
  OutputText? _textLarge;
  OutputText? _textRed;
  OutputText? _textLargeRed;

  @override
  void initState() {
    super.initState();

    _textNormal = NormalText();
    _textLarge = LargeText(_textNormal!);
    _textRed = RedText(_textNormal!);
    _textLargeRed = RedText(_textLarge!);
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  void _outputNormal() {
    setState(() {
      _outputColor = _textNormal!.getColor();
      _outputFontSize = _textNormal!.getFontSize();
      _outputText = _textNormal!.getText();
    });
  }

  void _outputLarge() {
    setState(() {
      _outputColor = _textLarge!.getColor();
      _outputFontSize = _textLarge!.getFontSize();
      _outputText = _textLarge!.getText();
    });
  }

  void _outputRed() {
    setState(() {
      _outputColor = _textRed!.getColor();
      _outputFontSize = _textRed!.getFontSize();
      _outputText = _textRed!.getText();
    });
  }

  void _outputLargeRed() {
    setState(() {
      _outputColor = _textLargeRed!.getColor();
      _outputFontSize = _textLargeRed!.getFontSize();
      _outputText = _textLargeRed!.getText();
    });
  }
  :
  : // 割愛
  :
```

以上