yojikのlog

yojikのブログです

いまさらFizzBuzz問題を[[OO厨]]がやるとどんなことになっちゃうのか

ちょっと気を取り直してプログラミングな話題を。
Lisp脳でやってみよう -- Schemeプログラマの発想をJavaでやるとどんなことになっちゃうのか
こちらを読んでて、逆に純度100%のOO厨気分で考えるとどうなるか考えてみた。
まずOO厨な考え方では「結果」を先を求める。1から100まで並んでるんだけど3の倍数の所は「Fizz」で、5の倍数のときはBuzzで、両方の倍数の場合はFizzBuzzが並んでる、FizzBuzzな仕様の「シーケンス」があればいい。シーケンス=繰り返し。ならばIteratorだ。

    while(fizzBuzz.hasNext()) {
      System.out.println(fizzBuzz.next());
    }

こんな感じのIteratorがあればいい。過程や!方法なぞ!どうでもよかろうなのだッ!
(中略)
ググってもそういうIteratrorがみつからないので、ようやく自分で作ることにする。ここで中略の時間が長すぎると面接に落ちることになる。

Iterator<String> fizzBuzz = new Iterator<String>() {
  public boolean hasNext() {}
  public String next() {}
  public void remove(){}
}

あとは肉付けしていくだけ。fizzBuzzには、内部にカウンタが必要で、hasNextでカウンタが数列の範囲に入ってるかどうかを判定して、nextが結果を返して、、、
ちょっとずつ説明するのが面倒になってきたので、一気に実装完了状態にしてしまう。

Iterator<String> fizzBuzz = new Iterator<String>() {
  Integer i = 1;
  public boolean hasNext() {
    return i<=100;
  }
  public String next() {
    String ret =(i%15==0)?"FizzBuzz":(i%3==0)?"Fizz":(i%5==0)?"Buzz":i.toString();
    i++;
    return ret;
  }
  public void remove(){/*実装いらないね*/}
};

あとは先程のwhile文でまわしてあげれば完成。ここでテストドリブンでやれれば素敵なチュートリアルになったのかもしれない。
ただ個人的にはnextの仕様がすごく気に食わない。現在の状態を返却するロジックと、カウンタを進めるというロジックが混在していて不自然だ。多分テストもやりにくいはず。これはOOというよりコマンド問い合わせの分離原則を守らないJavaAPIの問題だけど。
話を戻すと、まず結果の仕様(インタフェース)を決めて肉付けしていくという発想は、「リストの変換」という考え方のSchemeとは対極にあるかなと思う。ただしOOの方は、宣言的にできるわけじゃなくて、内部状態*1を持たなきゃいけない。さらに刺激*2を与えると内部状態が変わってしまう。でも「外側」から見ればそこら辺のゴタゴタは大体隠蔽されているということでバランスを保っているのだと思う。
とういうわけでネタっぽくやろうと思ったらマジメな地点に着地してしまった。
もちろん以上はちょっと旧世代のOO厨っぽい考え方で、RubyのブロックとかPythonのジェネレータみたいな言語レベルの支援があればそちらを使った方がいいわけですが。そもそもFizzBuzz程度ならなんでもいいんだけど。

*1:この場合はカウンタ

*2:この場合はnext()の呼び出し