破壊的OOP

関数型とOOPの話の続き。ちょっと議論をしてみたのだけれども、例えばこういうプログラムを考えてみよう。

class Mover1{
  int x;
  public Mover(int x){
    this.x = x;
  }
  public void move(){
    this.x++;
  }
  public int getX(){
    return this.x;
  }
}

さて、このコードを二つの方針で書き直してみる。

class Mover2{
  int x;
  public Mover(int x){
    this.x = x;
  }
  public Mover2 move(){
    this.x++;
    return this;
  }
  public int getX(){
    return this.x;
  }
}
class Mover3{
  int x;
  public Mover(int x){
    this.x = x;
  }
  public Mover3 move(){
    return new Mover3(this.x + 1);
  }
  public int getX(){
    return this.x;
  }
}

さて、なぜこのmoveメソッドをvoidからMoverに変更したのかと言えば、これによって、

Mover m = new Mover(1);
m.move().move().getX();

のように扱えるようになる。このほうが少なくとも便利だよね、という話がまずある。
次に、この二つのクラス、Mover2とMover3のどちらがbetterかという話もしなくてはならない。というか、こっちがメイン。で、例えばJavaであればMover2もMover3も可能だが、破壊的代入をしなければ、Mover2を使うことはできない。すなわち、関数型スタイルであればMover2はなるべく使わないと言う方針になる。どちらが良いのだろうか。
議論の対象となるのは3種類ある。一つ目は設計者としての視点。二つ目はこのクラスを使う側の視点。そして最後に、このクラスを作る側の視点だ。
まず設計者の視点としては、基本的にMover3を使うと言うスタンスを取れば、もしも何らかの事情(オーダーやそのクラス自体の特性)で内部状態を破壊的に持っていなくてはならないという場合にのみ特別にそのクラスを破壊的代入可能クラスとして定義してやり、基本的にはimmutableとしてやるという方針を取ることが出来る。これは、Mover2にするべきかMover3にするべきかという問題を考える手間を、回避することができる。良いsolutionだと思う。
では、使用者としてはどうだろうか。使用者としては、なるべく

Mover m = new Mover(1);
Mover next_m = m.move();

と形式でコーディングすることを心がけるのが良いのでは無いだろうか。これにより、使用者は、内部実装がどちらであっても良いというコーディングとなる。
最後に、クラスを作る側の視点だ。クラスを作る側としては、mutableな方が作りやすい。これは間違いが無い事実だ。あるいはnewをしないことによって動作のコストを抑えることができるだろう。また、内部状態として持っているフィールドを全てコンストラクタで渡せるようにしなくてはならない。そのコンストラクタの一部はprivate指定をしておく必要があるかもしれない。めんどくさい。
全体として、破壊的代入をしない方針と言うのは、一部の作りにくさを犠牲にして、バグを少なくすることを目的としているらしい、と言うことになる。で、OOPをする場合に、その作りにくさ、めんどくささは本当に有利に働くのかなあと言う気は依然としてある。