Enumまたはタイプセーフenumイディオムを使う – 定数を定義するためだけにインタフェースを使わない

時々、こういうコードを見かけないだろうか。



public interface XxxConst{

public static final String XXX_TYPE1 = "TYPE1";

public static final String XXX_TYPE2 = "TYPE2";

public static final String XXX_TYPE3 = "TYPE3";



public static final String AAA_ENABLED = "1";

public static final String AAA_DISABLED = "0";



//...つづく

}

このような「定数を定義するためだけのインタフェース」*1が私は嫌いだ。

理由は以下のとおり。

  • 最初に書いた人は良いけど、そこに何かが追加されるにしたがって、どこに何を追加すべきなのかを判断するのが簡単ではなくなっていく。使う方だってわかりにくい。そのinterfaceのメンバ数が100を越えて「XXX_TYPE1とXXX_TYPE1_CDって何が違うんだ? 僕はどっちを使えば良いんだ?」と皆に訊いてまわるのは嫌だ。(そして誰も答えられなかったりするし!)
  • こういうコードは、たいがいは片付けが貧弱で、せいぜいStringとintを使い分けてるだけっていうレベルだ。そんなコードを、コンパイルが必要なJavaでわざわざ書くというのは、なんか勿体ない感じがする。
  • 各定数に振る舞いを定義できないから、定数ごとに異なる振る舞いが必要になった場合は、その数だけ長い長いif,elseを書く必要がある。コードベースのあちこちに段々と増えていくそれを、全部もれなくメンテナンスするなんてウンザリする…。

「interface XxxConst方式は定義するのが簡単」という意見があるけれど、それは最初のうちだけだ。小さな書き捨てのプログラム以外では、すぐに今書いたような混乱が生じる。

では、どういう風に書けば良いのだろう?

Java5では、Enumが使えるようになった。それJ2SE1.4以前のバージョンでは、タイプセーフenumと呼ばれるイディオムを使って記述できる*2。このイディオムを正しく書くのはちょっと面倒だけど、EclipseのJETなどを使えばそれほどでもない。見返りのほうが断然大きい。

*1:標準ライブラリのBigDecimalクラスには似たような方法で丸め方法に紐付けられた定数が定義されている。あれは、丸め処理を書く必要にせまられた世界中のプログラマをイライラさせているのではないだろうか?

*2:Effective Java 98ページを参照

フォトリーディングをはじめた

まだ1週間くらいしかやっていないが、なかなか良い感じで気に入っている。

以前にも書いたとおり、フォトリーディングでは「目的を持って読む」ということがとても大切で、これを上手くできているときは潜在能力(?)が活用されるらしい。実際、準備段階でより具体的な目的意識をもてた場合は、より多くのものを得られる実感があった。

さらに、マインドマップを使ってアウトプットまでした場合は、普通に読むより理解が深まり、多くが頭に残るのも嬉しい。しかも速いので、今までは勿体無いことをしてきたな、と思っている。ただ、フォトリーディングフェーズまでは、お気楽な感じなのだが、アクティベーションフェーズ以降は、結構気合が必要だと感じた。疲れている時は、ちょっとヤル気がしないのが難点と言えば難点。

脇を締める – スコープは狭く

Java言語には、クラス、メンバ、そしてローカル変数のスコープを制御するための素晴らしい仕組みがある。

僕がまだ研修中のプログラマだった頃*1は「全部publicにしておけば困る事は無い。だって、後でアクセスしたくなるかもしれないし?」と思っていた。誰にもその間違いを暴露されないまま3ヶ月の研修を修了した僕は、人生最初の受託開発プロジェクトに参加し、テストで出た不具合の山を中程まで登った時にようやく、このちょっとしたプラクティスの重要性に気がついた(そして気がついた時には遅かったんだけど)。

たとえば、全部publicにしておくと、確かに書くのは楽だと思うし、実際そのように書いている時は楽だった*2。問題は、それで楽をできるのは書くときだけだということだ。その後に待っている不具合修正とか、仕様変更対応とかは、楽どころかかなり苦しくなる。そして、不具合修正が全く無いなんてそんな素晴らしいプログラマは控えめ言ってもほとんどいないし、仕様変更対応は、必ず必要になる*3。もっと悪いことに、書いた後のこういう作業のほうが、書く作業よりも遙かに多いという事実。

ここから他の色々なプラクティスの基礎となる指針が導かれる。すなわち、

書くときのことよりも、書いた後のことを考えて書いたほうが良い

ということだ。

スコープに話をもどそう。

スコープは小さい方が良い、というのが僕の主張だ。主な理由は以下の通り。

  1. 自分および共同作業している他人が、うっかり触って壊す機会を減らせる。
  2. コードの影響範囲が小さいので、バグや変更作業による予想外の副作用を減らせる。
  3. イマドキのIDEが備えている補完機能が表示する選択肢に、余計なものが混ざらないで済む。

どの理由もミスを減らすことに関係している。ミスを減らすことは、書いた後の作業を軽くするので、このプラクティスの効果の説明にもなる。

能書きの後に、あまり見かけないけど効果的なローカル変数のスコープ制限*4についてのありがちな例を書いておこう。

例として、Modelクラステストのフィクスチャなどで以下のようなコードを書く場合を考える。



final FooModel foo1 = new FooModel();

final BarModel bar11 = new BarModel();

final BarModel bar12 = new BarModel();

foo1.setBars(bar11, bar12);



final FooModel foo2 = new FooModel();

final BarModel bar21 = new BarModel();

final BarModel bar22 = new BarModel();

foo1.setBars(bar11, bar22);

このコードには1箇所、間違いがあるが、すぐに気付くだろうか?

賢いIDEの警告機能を全開にしていれば、警告くらいはしてくれると思うけど、まぁ普通は、すぐには気がつかない。レッドバーが「何かが間違っている」ことを教えてくれるけれど、それがテストフィクスチャのタイプミスだなんて事までは教えてくれない。そこでテスタは無実のテスト対象のコードの方にあらぬ疑いをかけたりすることになるわけだ。

そこで、ブロックを使って変数のスコープを限定してみる。



final FooModel foo1 = new FooModel();

{

final BarModel bar11 = new BarModel();

final BarModel bar12 = new BarModel();

foo1.setBars(bar11, bar12);

}



final FooModel foo2 = new FooModel();

{

final BarModel bar21 = new BarModel();

final BarModel bar22 = new BarModel();

foo1.setBars(bar11, bar22); //ここでコンパイルエラー bar11 cannot be resolved.

}

コメントの通り、エラーが出て、何が悪いのかすぐに分かって、テスト対象コードは無罪放免、テスタのあなたは残業なし、でめでたしめでたし、というわけだ。

*1:研修言語はVisualBasic6だったけど。

*2:なぜMicrosoftはデフォルトのアクセス修飾子をpublicにしなかったのかと不思議だったくらいだ

*3:諸行無常だ。

*4:クラスやメンバのスコープ制限については、色々な本やコーディング規約でよく説明されているので省略する

理解の秘密

現時点では、あまり読みたいところが無かった。

操作説明書とか書く時に読むと良さそう。

インストラクションの構成要素:

  • 使命:
    • インストラクションの意図、抽象的な目標についての情報。例えば「このパンをおいしくトーストする」
  • 最終目的:
    • インストラクションの具体的な目標についての情報。例えば「トースト前面がムラなくキツネ色に焼き上げること」
  • 手順:
    • 具体的な手順。例えば「オーブントースタの真ん中にトーストを入れて、蓋を締めてスィッチオン」
  • 時間:
    • 必要な時間(あるいはその予測)についての情報。例えば「おおよそ10分ほどで焼き上がります」
  • 予測:
    • 見通しについての情報。例えば「オーブンが暖まり、熱くなり、トーストが焼けていく筈」「目を離すと焦げてしまうかもしれない」 
  • 失敗:
    • 操作が失敗したこと、あるいは、失敗しつつあることを判断するための基準についての情報。例えば「キツネ色を通り越して茶色になっちゃったら失敗なのでやりなおしてください」とか。

「失敗」のところは、勉強になった。

ヒューマンエラーを防ぐ知恵

ヒューマンエラーを防ぐ知恵 (DOJIN選書 4)

ヒューマンエラーを防ぐ知恵 (DOJIN選書 4)

  • 作者: 中田亨
  • 出版社/メーカー: 化学同人
  • 発売日: 2007/03/20
  • メディア: 単行本(ソフトカバー)

最近、プロジェクトで、ポストモーテム/レトロスぺクティブ(振り返りプロセス)というのをやるようになった。

今のところは、まぁ前向きな話題が多くて、「失敗してしまった。何が悪かったのか?」なんて話題は出ていないけど、残念ながら、そういうのもすぐに必要になると思う。

そういう時って真剣に考えれば考えるほど、

→ひとつのミスに過剰反応

→それを防ぐ為にはどうすれば良いのか議論しよう!みたいな感じになったり。

→その結果、重すぎるプロセスが段々増えていって何かしんどい…

みたいなことにならないように要注意。

やっぱりリスク対応の”匙加減”というか、ポイントを押さえた対応というのが必要なんだと思う。言うのは簡単だけど、じゃぁ具体的に「ポイント」って何?という答のひとつが、この本の2ページ目に書かれていた。

しかし、事故はたった一つのきっかけだけで説明できるものではありません。事故は、複数の要素・要因がそろった結果生じるものです。これはちょうど、ポーカーや麻雀で薬がそろって上がることに似ています。「あのカードが手に入れば上がれた」とか「あの牌を捨てたことが敗因だ」という後悔は適切ではありません。麻雀の教科書を読んでみると、「両面待ちにしろ」とか「危険牌を振り込むな」と書いてあります。”最後のトドメ”である論された牌については、なんら原級していません。これに習えば、「事故は何面待ちになっていたか?」とか「危険牌を見極める方法は何か?」について書いたヒューマンエラーの本があってよいはずです。

「事故は何面待ちになっていたか?」 という「発生しやすさ(あるいは、発生しにくさ)の構造」という捉え方は、良いポイントかも。

以下はその他のメモ。

  • ポップアウト効果:目線の移動線からずれているものは目立つ。コーディングでも使える。
  • ありがた迷惑を誘発しそうな外観は改める。間違っているように見えるコードとか?
  • 覚えるより、思い出せるかが問題。
  • 左右の違いを厳守させたいのなら、エンジンの取り付け部分の形を左右で違うものにして、取り違えると取りつけられないようにするべき。引数の型とか?

あなたもいままでの10倍速く本が読める

あなたもいままでの10倍速く本が読める

あなたもいままでの10倍速く本が読める

目次よりも前書よりも前に、この本を25分、プラス30分、プラス45~90分で読むためのガイダンスが付いているところが面白い。

でも、「準備」のステップで、いきなりつまづいた。

効果的なリーディングは、明確な「目的」を持つことから始まります。「これは、読むことで自分が何を得たいのか」を具体的にする、ということを意味します。

とても納得の行く話なんだけど、僕にはちょっと難しい。

積読キューからFIFOで本を読んで行くと、ちょっと情けないけど、「この本、何で買ったんだっけ…」という事を先ず考えないといけない。

最近、職場で「人生の目標を達成するために、人生をプロジェクトとみなしてマネジメントしてみてはどうか」というような話を聞いた。自分の個人的な目標を明確にして、ブレイクダウンできていたら、「自分はこの本から何を得たいのか」なんて自問自答する必要も無いと思う。読書よりもそっちが先ということか。