イミュータブルにすることでCompositeパターンの循環参照を防いだはずだった

#はじめに & 概要
　デザインパターンの1つ、Compositeパターンに登場するオブジェクトをイミュータブルにしたら、循環参照を防ぐことができました。でも後で気づいたら、(多くの場合)全然使い物になりませんでした。

#前知識
##Compositeパターンについて(おさらい)
　[Compositeパターン](https://www.google.co.jp/search?q=composite%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)は、ディレクトリ階層のように入れ子状の構造を表すために使われるデザインパターンです。GoFの23のデザインパターンでも取り上げられ、「容器と中身を同一視」することが特徴です。
![composite.png](https://qiita-image-store.s3.amazonaws.com/0/156941/eb770283-2787-5494-ef1f-371a12e77062.png)
　上のクラス図では、`Composite`クラスがフォルダーなどの入れ物を、`Leaf`クラスがファイルなどのアイテムを示していて、`Component`抽象クラスを使うことでこれらを同一視しています。`Composite`は`Component`を要素として持ち、これにより`Leaf`も`Composite`も要素にすることが可能です。`Composite`に要素を追加するには`add(Component)`を使います。

##Compositeパターンで発生する循環参照について
　`Composite`クラスは、生成後も`add(Component)`を呼び出すことができるため、循環参照が起きる可能性があります。
　データベースの検索クエリーのようなものを考えて具体例で説明します。`Query`インターフェースは`judge(Object)`抽象メソッドを持ち、オブジェクトがクエリーに該当するかどうかを判定します。`KeyValueQuery`クラスは値とキーの組み合わせで表現されるクエリーです(おそらく、渡されたオブジェクトからkeyに対応する値を取り出し、valueと照合するのでしょう)。`AndQuery`クラスは複数の`Query`を子要素として持ち、すべての子要素であるクエリーに該当するかどうかを判定します。

![クラス図(Query_ ミュータブル).png](https://qiita-image-store.s3.amazonaws.com/0/156941/21e3b218-0594-68e0-d06c-a6615cf84356.png)

　循環参照が発生する状況を示すため、さらに具体化したオブジェクト図を描いてみました。Aの`AndQuery`が「もも」と「千葉県」の`KeyValueQuery`とBの`AndQuery`を子要素として持っています。Bもまた子要素を持ち、Cの`AndQuery`はさらにAを子要素として持っています。
![オブジェクト図(Query_ イミュータブル).png](https://qiita-image-store.s3.amazonaws.com/0/156941/76028d39-85a2-eb8d-453d-a5b85825f4bc.png)
　おわかりでしょうが、あるオブジェクトがAの`AndQuery`に該当するかどうかを調べるため、`A.judge()`を呼び出すと、BやCの`judge()`も連鎖的に呼び出され、また`A.judge()`が呼び出されてしまいます。Excelでよく見るあれです。
　しつこいでしょうが、処理の流れは以下のような感じです。

```java:Main.java#main
KeyValueQuery q1 = new KeyValueQuery("好きな食べ物", "もも");
KeyValueQuery q2 = new KeyValueQuery("出身地", "千葉県");
KeyValueQuery q3 = new KeyValueQuery("趣味", "プログラミング");

AndQuery C = new AndQuery(null);
AndQuery B = new AndQuery(q3, C);
AndQuery A = new AndQuery(q1, q2, B);
C.add(A);
```


##イミュータブルについて(おさらい)
　オブジェクトが生成されて以降変更されない(できない)ことをイミュータブルであると言います。たとえば、JavaのStringなんかはイミュータブルです。部分文字列を返すメソッドString#subString()は、**元の文字列を変更することなく**部分文字列からなる新しい文字列を返します。
　コンテナ(コレクション、リスト、配列などなど)でもイミュータブルなものを考えることができますね。コンテナを変更して要素を追加するのではなく、**すでにある要素に新しい要素を1つ加えた新しいコンテナを作る**と考えれば良いです。(ぜんぶ要素をコピーしなければいけなくて時間かかりそうだな... うまい方法もあるらしい [→イミュータブル - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB))

#イミュータブルなCompositeパターン
　さて、Compositeパターンに登場するオブジェクトをイミュータブルにすると、循環参照が防げそうです。先ほどの`AndQuery`クラスを改良してみました。
##例(Queryインターフェース)
![クラス図(Query_ イミュータブル).png](https://qiita-image-store.s3.amazonaws.com/0/156941/f0ea39a9-93d7-5579-0b3c-a832d3ee320a.png)
　`AndQuery`を改良して、イミュータブルにしてみました。`AndQuery`はnew時に子要素を与えて使います。もし、生成後に子要素を追加したい時には`and(Query...)`を呼び出して、子要素が追加された新しい`AndQuery`を作ります。
　これだけで先ほどのような循環参照は起きなくなります。というか**循環参照するように書けなくなります**。試してみましょう:

```java:Main2.java#main
KeyValueQuery q1 = new KeyValueQuery("好きな食べ物", "もも");
KeyValueQuery q2 = new KeyValueQuery("出身地", "千葉県");
KeyValueQuery q3 = new KeyValueQuery("趣味", "プログラミング");

AndQuery C = new AndQuery(null);
AndQuery B = new AndQuery(q3, C);
AndQuery A = new AndQuery(q1, q2, B);

C = new AndQuery(A); //変数Cの指すオブジェクトが変わるだけで、Cの実体が変更されるわけではない
C.and(A); //Cの実体は変更されず、Aを子要素に持つ新しいオブジェクトができるのみ
```

　なるほど、確かに循環参照するようには書けなくなりました。先ほどはまだ中身がないコンポジットをとりあえず作っておくことができましたが、今度はそれがほとんど意味なくなっています。これで万事解決... なのかな？

##問題点

###後からコンポーネントを修正したり、コンポジットに追加できない
　このパターンだと、あるコンポーネントを修正してもコンポジットは修正前のコンポーネントを参照し続けるため、後からコンポーネントを修正することができなくなってしまいます。コンポジットにコンポーネントを後から追加した場合も同様です。
　いや、厳密にはできます。1つずつコンポーネントを修正できないのです。ある時点でコンポーネントを修正しようと思ったら、影響のある(=子要素としてそれを含む)すべてのコンポジットに「ごめ～ん、私変わったので、(同じ階層の)仲間をすべて含んだ新しいコンポジットに生まれ変わって:two_hearts:」と頼めばいいのです。~~できるならの話ですが。~~

追記: できるっぽい。上の[イミュータブル - Wikipedia]( https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB)のページなんかでは、うまいことやってますね。
###使いどころあるの？
　比較的小規模であったり、**階層の全体像が生成時点で見えていて**修正が不要なときには使えそうです(といっても、その程度の規模なら「気をつける」だけで循環参照を防げそうにも思えるが)。例に検索クエリーを持ち出したのはそれが理由で、これらの性質を満たしています。ディレクトリ階層のスナップショットとかもいけるかも。
###本当に循環参照しない？
　今のところこのパターンで循環参照させる方法が思いつかないです。匿名クラスとか使えば書けちゃうかもしれません(でもそれってもはやイミュータブルじゃないのでは)。気づいたらコメントください。
#参考資料
- 結城浩「増補改訂版 Java言語で学ぶデザインパターン入門」ソフトバンククリエイティブ