読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

オブジェクトの状態は作らない

ここで言う状態とは、クラス変数と振る舞いの状態を意味します。
例えば、クラス内の特定のフラグが true となったらメソッドの挙動が変わるなどです。

状態とは?

Java で例えれば、Closable インターフェース実装クラスなんかがそれに当たります。
リソースの解放と言う意味では仕方ないのでしょうが、close() を実行したあとでメソッドを呼び出すと例外を吐き出すようになります。

リソースを解放した後にアクセスできないのは当然の話ですし、close 後は使用不可(例外)を吐き出すだけまだマシと言うものでしょう。
一番怖いのは「動作が変わる」と言うパターンです。

例えばクラス自体が状態遷移図のようなものを持っている場合です。

状態遷移図(Wikipedia)

何故状態が悪?

オブジェクト指向の成り立ちが、もともと シミュレーション用言語 Simula なので、状態の変化と言うのはあって当然だったのでしょう。
また、現実的にも自販機などのようにお金を投入された状態(ドリンク選択待機状態)など、状態を持つものは多いでしょう。

また、オブジェクト指向の本質、抽象化による現実の模倣としても、状態を是としています。
ですが、いくつかの理由で、これはバグを産みやすくなります。

  1. 状態を持ったオブジェクトはテストしづらい。
    例えば、クラス内の状態を2変数で持ったとしましょう。すると、「テストすべき状態数=1変数で操作される状態数 x 1変数で操作される状態数」と倍数的に増えてしまいます。
    まさかテストせずにリリースする訳にも行かないでしょう?
    テストコードを書かないとしても、結合テストでは、ここの分岐を動かすための操作、事前条件を整えるのが難しくなります。
    また、結合レベルでは見逃す事も多いでしょう。
  2. 安心して他のメソッドに渡せない。
    オブジェクトに状態があり、それが書き換え可能だと、メソッド引数に入れて渡した時、その先で書き換えられてしまう恐れがつきまといます。
    それを避けるために、全ての手続きを追いかける必要性をプログラマに課してしまい、その行き着く先は1メソッドに全て記述する巨大な手続きです。
    多くのプログラマが全力で嫌がるCOBOLバッチの世界へようこそ…。
  3. 常にコード内でオブジェクトの状態を確認しなければならない。
    メソッドによってオブジェクトの状態が変わるという事は、メソッドの呼び出し順序にすら制限しかねない不安定なものになります。
    次に待っているのは、その状態をフルコントロールするための状態確認処理の増加です。
    それはビジネス的に本質的なものではなく、可読性を損ないます。

つまり、状態を持てば持つほど、それを使用するプログラムはそのコントロールに手間を裂く必要に迫られ、複雑化を招いてしまいます。
これは保守性とあい入れる状態ではありません

どう避ける?

私はこの問題に対し、 「クラス変数を使わない」 という方針をとります。
代わりに使用するのが 「クラス定数」 つまり、 コンストラクタによる初期化以外の値の変更を拒絶する 方針です。

こうすれば、コンストラクタによってクラスの振る舞いは確定されるので、下記のようなメリットがあります。

  • テストは書きやすい
  • 気軽にメソッド引数に渡しやすい
  • メソッドの呼び出し順序生が発生しにくい(DB など、外部出力があるとまた少し違いますが)
  • 自分のメソッド内(もしくは自分のクラス内)で初期化したオブジェクトの振る舞いは固定なので、振る舞いを当てにしたコードを安全に書ける

欠点も挙げないと不公平でしょうから、欠点も挙げておきます。

  • 別の状態のオブジェクトを作るために、new が必要となってしまう。
    昔ながらのプログラマは、「それではメモリの確保/初期化コストが高いのではないか」と考えるかもしれません。
    最近の Java/C# のランタイムは優秀で、変数を使わない場合の new コストはかなり低くなってきています。
    少なくとも、ハードウェアの制約、ゲーム業界ほどの性能厨でもない限り、気にするべき問題ではありません。
  • 状態で物事を考えるという考え方を捨てなければならない。
    はい、この考え方はどちらかと言えば「関数型言語」の思考に近いです。
    慣れるには時間がかるかもしれません。

しかし私はこの学習コストを必要悪だと考えます。
理由は現在発生中のパラダイムシフトです。
C++/Java は後発の方でしょう、C#, Scala などのように「オブジェクト指向 & 関数型」のハイブリッド化が時流です。

かつて手続きにしがみ付いたプログラマ老害呼ばわりされたように、静的に処理が決定できる関数型の概念理解が必須となる時はそう遠くないところにあるでしょう。

皆気張りましょう!