Immutable のメモ


Immutable は以下の場合に使用できる

  1. インスタンスの生成後、状態(メンバ)が変化しないとき
    • このとき、setter メソッドが無いというだけでは不十分で、メンバが参照している先のインスタンスも変化しない必要がある。
    • C++ なら全てのメソッドが const であれば良いということになる(const_cast や mutable を使うともちろんダメだけど)。
  2. インスタンスが共有され、頻繁にアクセスされるとき
    • synchronized によるコストが大きいと感じるぐらいまでに頻繁にアクセスされる場合に使う価値がある。


setter メソッドがあっても Immutable パターンを適用するのを諦める必要はない。
もし setter がよく使われる局面と全然使われない局面に分かれているのであれば、そのクラスを mutable なクラスと immutable なクラスに分け、setter をよく使う局面では mutable なクラスを使い、全然使わない局面では immutable なクラスを使えばいい。
具体的には、mutable なクラスは setter で自分自身を書き換えるけど、immutable なクラスは setter が呼ばれると自身を新しく作り直して新しいインスタンスを返すようにする。自分自身は決して変化させないようにすればいい。
Java なら java.lang.String(immutable)と java.lang.StringBuffer(mutable)がいい例。
C# なら System.String(immutable)と System.Text.StringBuilder(mutable)がいい例。
ちなみに VC8 の std::string は mutable になっている(実装はプラットフォーム依存なので、他のプラットフォームでは immutable になっている可能性もある)。


Immutable なクラスはちょっとしたこと(setter 作ったり継承してフィールドにアクセスしたり)で不変性が崩れるので、注意する必要がある。
しっかりとドキュメントに記述しておくことが重要。
また、以下の場合において不変性が崩れることがある。

  1. メンバが保持しているインスタンスを、そのまま getter メソッドの戻り値にした。
  2. コンストラクタに引数として渡されたインスタンスを、そのままフィールドに代入した。

C++ の場合は、インスタンスというのは実体を参照しているオブジェクト(ポインタや参照、boost::shared_ptr<>)のことになる。普通に実体を渡す分にはコピーされるので問題ない。


java.util.concurrent.CopyOnWriteArrayList というクラスは、書き込みするときにコピーするクラスらしくて、書き込みを行うと全配列をコピーして書き換えることによって immutable にしている。
必要になれば C++ で作ってみるのもいいかも。