Loading...

Composite

和田 卓人 (a.k.a id:t-wada or @t_wada)

Press key to advance.

Slides controls, press:

  • and to move around.
  • Ctrl/Command and + or - to zoom in and out if slides don’t fit.
  • T to change the theme.
  • H to toggle syntax highlight.

自己紹介

  • 名前 : 和田 卓人(わだ たくと)
  • ブログ : id:t-wada
  • Twitter : @t_wada
  • github / facebook : twada
  • タワーズ・クエスト株式会社 取締役社長

./../../images/TQ_LOGO_SMALL.png

プログラマが知るべき97のこと

よろしくお願いします

Compositeをひとことで

「一つ」と「複数」を同じように扱いたい

Composite

  • 部分と全体の同一視
  • 再帰的な構造の構成
  • 1,2,..たくさん
  • OCP を守るのでクライアント側コードに優しい

Composite と関連

GoF 本における Composite

./../../images/GoF_jp.jpg 部分一全体階層 を表現するために、オブジェクトを木構造に組み立てる。 Composite パターンにより、クライアントは、 個々のオブジェクトとオブジェクトを合成したものを一様に扱う ことができるようになる。

Composite 構造

動機

./../../images/GoF_jp.jpg たとえユーザがプリミティブなオブジェクトとコンテナのオブジェクトを同様に扱っていても、 これらの クラスを使用するコードは、これらのオブジェクトを区別して扱わなければならない。 このようにオブジェクトを区別しなければならない場合、アプリケーションはより複雑になる。 Composite パターンでは、クライアントがこのような 区別をする必要がなくなる 再帰構成の使い方について説明する。

適用可能性

./../../images/GoF_jp.jpg 次のような場合に、 Composite パターンを使用する。

  • オブジェクトの 部分ー全体階層 を表現したい場合。
  • クライアントが、 オブジェクトを合成したものと個々のオブジェクトの違いを無視できるようにしたい 場合。このパターンを用いることで、クライアントは、 composite 構造内のすべてのオブジェクトを一様に扱うことができるようになる。

「奥義本」における Composite

例:SensorとCommand

複数のCommandを扱いたい

Compositeの導入

「奥義本」における Composite

./../../images/AgileSoftwareDevelopment_PPP_2nd_jp.jpg

たとえば、オブジェクトのリストやベクタ(配列)の代わりに Composite を使えるケースがあるはずだ。 このことを別の言い方で説明するとこうだ。 もともとは 1対1の関係だった Sensor と Command の関係を、1対多の関係に変えようとしたわけである。 ここで Composite を使うことで、 1対多の振る舞いを1対多の関係を使わずに実現した のだ。 1対1の関係は1対多の関係よりも ずっと理解しやすい し、 コーディングも楽 だし、 保守も簡単 だ。 したがって、これは明らかに適切なトレードオフだったと言える。 現行のプロジェクトに Composite を使うと、一体どれだけの1対多の関係が1対1の関係で済んでしまうのか興味深いところだ。

「奥義本」における Composite

./../../images/AgileSoftwareDevelopment_PPP_2nd_jp.jpg

もちろん、 Composite を使っても、すべての1対多の関係を1対1の関係に置き換えることはできないだろう。 Composite パターンの候補になるのは、 リスト中のオブジェクトが全く等価に取り扱われるものだけに限られる からだ。 たとえば、従業員のリストを管理し、今日が給料日の従業員を検索するような場合は Composite パターンを使うべきではない。全ての従業員を全く等価に取り扱っているわけではないからだ。

「奥義本」における Composite

./../../images/AgileSoftwareDevelopment_PPP_2nd_jp.jpg

それでも、 Composite パターンを適用できる1対多の関係は非常にたくさん見つけることができる。 そのメリットは絶大だ。 リストを管理するコードや繰り返しのためのコードを各クライアントに個別に埋め込むのではなく、 Composite クラスの中にたった一度だけ記述するだけ で済んでしまうからだ。

TDD 本における Composite

./../../images/TDD_JP.jpg p.175

オブジェクト群の振る舞いの組み合わせを1つの振る舞いとして持つオブジェクトをどのように実装するのか。 → コンポーネントオブジェクトのインポスタとなるオブジェクトを作成する。

例: 口座と取引

Account(口座) と Transaction(取引)

Transaction は増加額を格納する(実際は遥かに複雑で重要だが)。

Transaction(Money value)
    this.value = value;
}

AccountはTransactionの値を合計することで、残高を計算する。

Transaction[] transactions;
Money balance() {
    Money sum = Money.zero();
    for (Transactions tran : transactions) {
        sum = sum.plus(tran.value);
    }
    return sum;
}

複数の口座に対応したい

AccountとTransactionが同じインターフェースを実装したらどうなるのか, そのインターフェースをHoldingと呼ぶことにする。それよりよい名前をすぐには思い浮かばなかった。

interface Holding {
    Money balance();
}

Transaction はその値を返すことで、 balance() を実装することができる。

Money balance() {
    return value;
}

コンポジット

Account は Transaction ではなく、 Holding から構成されるようにすることができる。

Holding[] holdings;
Money balance() {
    Money sum = Money.zero();
    for (Holding holding : holdings) {
        sum = sum.plus(holding.balance());
    }
    return sum;
}

OverallAccount での問題は消滅する。 OverallAccount は Account を含む Account に過ぎない。

コンポジット

./../../images/TDD_JP.jpg

コンポジットの悪臭は前述の例でも登場している。 現実世界では、 Transaction には残高は存在しない。 コンポジットの適用はプログラマのトリックで、プログラマ以外には通常理解されない。 しかし、プログラム設計へのメリットは膨大なので、概念上の断絶を補うだけの価値がある。 Folder を含む Folder 、TestSuite を含む TestSuite 、Drawing を含む Drawing 、 現実世界に対応する概念は存在しないが、これらは すべてコードをシンプル にする。

コンポジット

./../../images/TDD_JP.jpg

コンポジットを使用すべき場所と使用すべきでない場所を見つけるまで、 筆者はコンポジットを長い間試さなければいけなかった。

この議論から明らかなように、筆者はまだ、 オブジェクトのコレクションがただのオブジェクトのコレクションである時と、 本当のコンポジットになっている時とを 明確に区別することはできない。

幸い、読者はリファクタリングが得意になるので、 重複が現れた瞬間にコンポジットを導入 し、 プログラムの複雑性が解消することを確認できる。

RtP における Composite

./../../images/RtP_JP.jpg

  • Builder による Composite の隠蔽 (100)
  • Composite による暗黙的なツリー構造の置き換え (188)
  • Composite の抽出 (226)
  • Composite による単数・複数別の処理の置き換え (236)

不吉な臭い

./../../images/RtP_JP.jpg 重複したコード

  • Composite による単数・複数別の処理の置き換え (236)
  • Composite の抽出 (226)

基本データ型への執着

  • Composite による暗黙的なツリー構造の置き換え (188)
  • Builder による Composite の隠蔽 (100)

リファクタリングの向き

./../../images/RtP_JP.jpg Composite を取り入れるリファクタリング

  • Composite による暗黙的なツリー構造の置き換え (188)
  • Composite の抽出 (226)
  • Composite による単数・複数別の処理の置き換え (236)

Composite から離れるリファクタリング

  • Builder による Composite の隠蔽 (100)

君の銀河もきっと輝く

TDD 本における Composite

./../../images/TDD_JP.jpg

p.163

しかし、Design Patterns という書籍の成功は、パターン表現の多様性を抑制している。 この書籍は設計フェーズに若干偏っているようだ。 確かに設計アクティビティとしてのリファクタリングを肯定していない。 TDD での設計では、デザインパターンに対する見方を若十変更する必要がある。

p.164

  • インポスタ(Imposter) 既存プロトコルの新しい実装を導入することで、バリエーションを導入する。
  • コンポジット(Composite) オブジェクト群の振る舞いの組み合わせを1つのオブジェクトで表現する。

十分シンプルなようだ。

  • Transaction には値がある
  • Account には残高がある

インポスタ(Imposter) - 詐欺師

./../../images/TDD_JP.jpg p.173,174

処理に新しいバリエーションをどのように導入するのか。

  • 既存のオブジェクトとプロトコルは同じだが、実装は異なる新しいオブジェクトを導入する。

以下はリファクタリングの際に出現するインポスタの例である。

  • ヌルオブジェクト - データの不在をデータの存在と同じように扱うことができる。
  • コンポジット - オブジェクトのコレクションを単独のオブジェクトと同じように扱うことができる。