| 著作一覧 |
追っ手から逃れるシステム(きしだのはてな)は、具体的な逃げ手のノウハウが満載でとても役に立つのだが、1点ダウトがある。
上から下へ流してもうまくやることで追っ手の戦意を著しく削ぐことが可能だからだ。
たとえば、以下のようにやって見せることだ(実は目撃談だったり)。
public class ValueObject {
String key1;
public String getKey1() { return key1; }
...// keyが5個くらいあるとする
}
...
// 幹のロジック(追記:後で書いていることとメソッド名が合わなくなっているので修正した。っていうか難しいよこの書き方は)
public void trunk(ValueObject v1, ValueObject v2) {
...
if (v1.getKey1().equals(v2.getKey1())) {
if (v1.getKey2().equals(v2.getKey2())) {
...
if (v1.getKey5().equals(v2.getKey5())) {
... // 処理タイプ1が20行
} else if (v1.getKey5().compareTo(v2.getKey5()) > 0) {
... // 処理タイプ1'が20行
} else if (v1.getKey5().compareTo(v2.getKey5()) < 0) {
... // 処理タイプ1''が20行
} else {
...(追記:もちろんここにも処理タイプ1が20行入っているのが不安感を煽るのなんのって)
}
...
} else if (v1.getKey2().compareTo(v2.getKey2()) > 0) {
// 処理タイプ1'が20行
} else if (v1.getKey2().compareTo(v2.getKey2()) < 0) {
// 処理タイプ1''が20行
} else {
// 処理タイプ1が20行
}
} else if (v1.getKey1().compareTo(v2.getKey1()) > 0) {
// 処理タイプ1'が20行
} else if (v1.getKey1().compareTo(v2.getKey1()) < 0) {
// 処理タイプ1''が20行
} else {
// 処理タイプ1が20行
}
...
// すごくたくさんの処理
if (v1.getKey1().equals(v2.getKey1())) {
if (v1.getKey2().equals(v2.getKey2())) {
...
if (v1.getKey5().equals(v2.getKey5())) {
... // 処理タイプ2が20行
} else if (v1.getKey5().compareTo(v2.getKey5()) > 0) {
... // 処理タイプ2'が20行
}
...
}
// すごくたくさんの処理
if (v1.getKey1().equals(v2.getKey1())) {
if (v1.getKey2().equals(v2.getKey2())) {
...
if (v1.getKey5().equals(v2.getKey5())) {
... // 処理タイプ3が20行
} else if (v1.getKey5().compareTo(v2.getKey5()) > 0) {
... // 処理タイプ3'が20行
}
...
}
// すごくたくさんの処理
if (v1.getKey1().equals(v2.getKey1())) {
...
// この調子でずーっと永遠とも思える行数が使われて行く
}
追記:最初、すさまじく複雑な条件なのかと思ったらなんのことはない、単に2つの引数が小さいか、等しいか、大きいかで処理が分岐しているだけの話なのに1つのif文につき1条件として(複合キーだからこれはわからんでもない)、かつ律儀にすべてのブロックに処理を埋め込んでいるだけの話だ。それを何度でも倦むことなく繰り返しているのは条件と条件の間に共通の処理が時間軸に沿って挿入されているからのようだ。
これを究極のコピー&ペースト技と呼ぼう。確かにパターンを掴めば上から下へ流れるだけだが、実は処理タイプn〜n'''のいずれかに、しかしkey3が等しかった場合「だけ」は処理タイプn.5が必要とかが紛れてみたらいかがなものか(ry。
逆側から考えると、次のようになっていれば追跡しやすいと考える。
まず、処理1〜処理嘘800までは、ばらかしているということから、仕様上の1つの処理の固まりと考えられる。したがって、ここは必ずしも幹上で上から下へ入れる必要はない。次にn.5が必要となるのは極一部だから、そこだけを特別扱いすれば良い。最後に、比較を幹で行うことは良いとしても、比較の実装はValueObjectが持つべきだ。別のクラスであってもこの場合は文字通り関連があり、いずれにしろ追う場合、エディターにはこのソースも開いているはずだ(マルチファイル/ウィンドウエディターであれば)。
public class ValueObject implements Comparable {
...
public int compareTo(Object o) {
... // 追加(もちろん特化メソッドでも良いが僕は標準が好き
}
}
...
public void trunk(ValueObject v1, ValueObject v2) {
...
int v1CompareToV2 = v1.compareTo(v2); // 注:基本的にローカル変数は1〜2字ルールなのだが、こういう分岐フラグとかは別格
if (v1CompareToV2 > 0) {
処理パターン1'();
すごくたくさんの処理1();
処理パターン2'();
すごくたくさんの処理2();
...
if (v1.getKey3().equals(v2.getKey3()) {
処理パターン4'改(do4.5);
} else {
処理パターン4'改(dont4.5);
}
処理パターン5'();
...
} else if (v1CompareToV2 < 0) {
処理パターン1''();
すごくたくさんの処理1();
処理パターン2''();
すごくたくさんの処理2();
...
} else {
処理パターン1();
すごくたくさんの処理1();
処理パターン2();
すごくたくさんの処理2();
...
}
}
もちろん、処理パターン1〜1''が引数レベルで調整可能な良く似た処理であれば、それもありかも(分離性から言えばなくても良いと思う)。
だから、とてつもなく逃げるのがうまい人は上から下までルールでさえ、とんでもないことをするということでした。
追記:なお、上の形を最終型とせずにテンプレートメソッドパターンにしてクラスを分離すると突然読みにくくなったりするのがおもしろいところ。でも僕はケースバイケースでやるけどね。ただ、この場合であれば内部クラスとして閉じるけど。
追記:修正した。いや、実に読むのも辛いが書くのも大変だ。作ったやつはおそろしく頭が良いに違いない。使い方が間違っているだけなんだろう。
追記:本当にこれは反省しているのだが、CheckStyle嫌いというのもあって、また、明らかにそうすべきケースがあるからなのだが、明示的な行数制限を取っ払ったのが裏目だ。しかし、明らかにそうすべきケースであってもそれを判断できる開発者ならその制限を回避することはできるわけだから、メソッドの行数のようなものは制限をきつめにかけるべきだったのだ。以前、80点から100点ルールに合わせるべきと書いたが、まさにケースバイケースで、50点ルールにすべきものもあるということだ。と書いてから気付いたが最初のきしださんのネタは20行ルールを逆手に取るうまい逃げ手の話だった。あちら立てればこちら立たずになってたのかも知れないってことか。だめなやつはどうやらせてもやっぱり……という結論にするのは気に食わないから、ここはやはりだめなものとだめじゃないものを教えてくしかないという結論を出さざるを得ないんだろうか。
ジェズイットを見習え |