ホットスポットを実装する
[リーダブルコード]コメント:ライターズブロックを乗り越える
これがJavaであれば、私ならテンプレートメソッドパターンで局所化しプログラム完了の遅延を図る。
テンプレートメソッドパターンではフックメソッドの実装を下位クラスに委ねる。
このパターンは処理の一部がホットスポットである場合によく用いられる。ホットスポットとは仕様変更が入りやすい、或は仕様が曖昧な箇所のことである。
例えば、CSVファイルを取り込みDBにロードしたいがCSVファイルのデータを配列に変換するにはどうすれば良いかが分からない、という状況で使う。
CSV形式ではデータがカンマを含む場合、ダブルクオテーションでくくるというルールがある。しかしこれはカンマがあるデータだけというルールではないため複雑なプログラムとなる。APIもなしにこれを実装するのは大変である。
そこで「データ値にはカンマもダブルクオテーションも入らない」という前提でとりあえず実装する。
まずホットスポットである「CSVファイルを配列に変換する処理」をフックメソッドとして局所化する。コメントには「CSVファイルの各データを配列として返す」と記する。ただしこのメソッドは中身のない空メソッドもしくは抽象メソッドである。
次に子クラスにてフックメソッドを実装する。このコメントには「CSVファイルの各データを配列として返す(ただし各データ値にはカンマは含まれないものとする)」と記し、その通りの実装を行う。こうすることでデータ値にカンマ入るという仕様にも対応し易くなる。
例を示す。
abstract class Helper {
/** CSVファイルの各データを配列として返す/*
abstract String getCsvData(File file);
}
class HelperImpl extends Helper{
/** CSVファイルの各データを配列として返す(ただし各データ値にはカンマは含まれないものとする)/*
String getCscData(File file){ ... }
}
<component name="Helper" class="HelperImpl"/>
これは「CSVにはカンマが入らない」前提のプログラムであるが「やはりCSVにはカンマが入ることになりそうだ」と云われても簡単に対応することができる。具体的には以下のクラスを定義しdiconファイルを修正する。
class HelperImpl2 extends Helper{
/** CSVファイルの各データを配列として返す/*
String getCscData(File file){ ... }
}
<component name="Helper" class="HelperImpl2"/>
可視性の高いコード
[リーダブルコード]コメント:情報密度の濃さ:正確に書く
なかなか面白いことを書いている。
もしも私がプログラマで、コードレビューでこのような指摘がでたらなかなか厳しいレビューアだなと思うだろうが確かにその通りである。行数カウンタなどの機能においてもこの観点は重要である。ただ、本来これは設計レベルの話でプログラマにそれをコメントする責任を負わせるのは酷かもしれないとは思うが。
だが、記事にもあるがそれは関数のヘッダコメントの話である。コード文中のコメントであれば長々と書くべきではない。長々と書かなければならないのであれば関数化しなければならない。コード文中のコメントを長々と書くとプログラムの可視性を著しく落とすのである。たとえ関数の中身が一行になろうとも関数化が必要だ。
また曖昧さはコード文中であろうと関数ヘッダコメントであろうと完全に除去せねばならない。下手に「エレガントな文章の方が分かりやすい」などと砕けた文章で格好つけたり、読み手に解釈を強いる曖昧さを残す文章にしては「絶対に」いけない。もしも曖昧なコメントがあれば冗長でもいいから正確に書くべきだ。そして必要なら関数化する。
このようにリファクタリングを行うと最終的にはコード文中からコメントがどんどん消えていく。コード文中のコメントが関数のヘッダコメントに変換されていくのである。結果可視性は向上していく。
「読める」コードにはそのような工夫が必要である。
経験をもっとすべし
色んな人が「オブジェクト指向やデザインパターンを中途半端な知識で使うな」という趣旨の事を仰っています
正論です。パターンや設計手法は「考え方の見本」であり、それらを考えなしに使うというのは本末転倒です
けれど私はやはり、
「やりたいならどんどんオブジェクト指向やデザインパターンをやったみたらいい」
と言いたいです。
オブジェクト指向にこだわったせいでクソコードを書いてしまうような経験の浅い人は、たとえオブジェクト指向を禁止されてもクソコードを書いてしまいます。
オブジェクト指向プログラミングの経験不足が今のプログラマに否めないのは、彼らの責任ではない。彼らの現場がオブジェクト指向開発でなかったり、彼らの先輩、上司がオブジェクト指向に疎いからである。
本来プログラマは好奇心が強い。技術習得のために講習会に出たり社内勉強会を積極的にするとかしたいのである。然しそうした技術習得をしてもその先に使える可能性がほとんどないのであればその努力は無駄となる。それではモチベーションはあがらない。
であれば営業はできるだけ新技術を使った開発を受注したり、あるいは管理者も自ずから新技術を学びそれを実際のプロジェクトに適用する。同時にプログラマを講習会にいかせたり社内勉強会を支援するなどの企業努力が必要である。
然しそれでも未経験というリスクがある。
例えばデザインパターンはよい設計のテンプレートだが経験がないとうまく使えない。経験不足で使うと逆効果となるリスクがある。これは未経験の者が誰でも通るどうしようもない壁である。
であれば仮想プロジェクトで経験すればよい。
最初の経験をリスクなくこなせるのが仮想プロジェクトである。いや、リスクはあるがそれが顕在化したところで損害はない。これは仮想プロジェクトの強みである。こうした仮想プロジェクトへの参加は講習会などで経験できる。
或は自社で実験プロジェクトを企画し経験してしまうのもよい。
そうやって次回の本番までに経験しておく。それでリスクは減るのだ。もちろんリスクがゼロになることはない。だがそれは経験数が増えても同じである。だからとりあえず一度経験したら本番プロジェクトにチャレンジすればいい。
然し、そうした仮想プロジェクトに参加するチャンスのないプログラマもいるだろう。そういうプログラマはどうすればいいのか。
「やってしまう」しかない。
賭けである。
もちろんそのためには最低限しなければならないことがある。
リスクを承知でやるとしてもそれを回避するためのリスクマネージメントをしなければならないということだ。それを調べることが経験の代わりになる。
当たり前だがお客様の損害のリスクヘッジを最優先にしなければならない。そのためには工夫も必要だ。失敗しリスクが顕在化した場合の覚悟も要る。無間残業地獄が待っているかもしれない。下手すればクビである。その上で「使ってみたい」という欲望にかられたなら使うしかないではないか。なら、使え!使ってしまえ!
リスクシミュレーションをしながら真剣に技術を研究し挑戦するというぎりぎりの状況でこそプログラマは常識では考えられない飛躍的な技術的躍進に導かれるのだ。
そしてこれは多くのプログラマが経験する道でもあるのだ。
猿でも分かる継承の作法
これは危険な考え方だ。別に嘘を書いている訳ではないがこのような説明がしばしばトラブルの原因となる。よい継承には作法があるのだ。
例えば警察官というクラスがあったとする。
警察官の仕事はいろいろだ。お巡りさんのような制服警官の仕事はパトロールであり科捜研であれば証拠分析だろう。刑事なら犯人確保。それぞれ仕事の内容は違う。
だが共通点もある。それは「パトロール」という仕事も「証拠分析」という仕事も「犯人確保」という仕事も「町の安全を守る」という仕事の一種である、ということだ。
具体的なコードでかけばこうなる。
class 警察官{
void 仕事(){町の安全を守る} 👈この仕事が町のパトロールとは限らない
}
class お巡りさん extends 警察官{
void 仕事(){町のパトロールをする} 👈この仕事は町の安全を守る仕事である
}
class 科捜研の人 extends 警察官{
void 仕事(){証拠分析をする} 👈この仕事は町の安全を守る仕事である
}
class 刑事さん extends 警察官{
void 仕事(){犯人を確保する} 👈この仕事は町の安全を守る仕事である
}
これが継承の作法を守った「よい継承」の例である。
では「悪い継承」とは何か。
例を示そう。こんな継承はすべきではない。
class 泥棒 extends 警察官{
void 仕事(){他人の家に侵入し金品を奪う} 👈この仕事は町の安全を守る仕事ではない
}
できるだけ分かりやすく説明したつもりだが、これではよくわからない、という方もいるだろう。そんな諸君は是非ともメイヤーのオブジェクト指向入門を一読してほしい。
オブジェクト指向入門という本
ラムダについての懸念
JAVAの新機能ラムダ。関数型プログラム到来と絶賛されているが私はこの評価に戸惑いを覚える。そもそもこのラムダは関数型プログラムにパラダイムシフトできる人間だけがそのメリットを教授できる。一介のプログラマが手を出せる代物ではない。だが技術者にとって最先端の技術ほど魅力的なものはない。最先端であればよく分からないままに使ってしまうのが技術者の悪い癖である。
かつてオブジェクト指向に憧れ、パラダイムシフトしないまま手を出したプログラマがどれほど居たことか。そしたそれは、そのまま負の資産となった。彼等の作ったその負の資産は今だに保守担当技術者を苦しめている。
ラムダもその運命を辿るのではないか。また同じ過ちを繰り返さないことを願うばかりだ。
バグとは何か
「仕様と違う結果」が起きる原因をバグと云う。バグはエラーとは異なる概念だ。
ログイン画面を考えてみよう。
ユーザーIDとパスワードを正しく入力し、ログインボタンを押すとトップ画面に移る。これは正常系というログインの本来の事象だ。
これに対し、ユーザーIDかパスワードを間違って入力しログインボタンを押すとエラーとなる。これは異常系というユーザーの想定外の事象である。またある事象が異常系である条件は
・エラー画面に遷移する
・エラーメッセージが出力される
のどちらか若しくは両方が満たされることと云える。
ではバグとは何か。
正常系であれ異常系であれ、テストに於いてその事象が設計通りの結果にならない時、そこにはプログラマの想定外の何らかの問題があると云える。この問題をバグと云う。バグを発見した場合プログラマはテスト後に直ちに故障票を書いて直す必要がある。
バグはテストだけでは捕捉できない。レビューが必要である。レビューをしなければ問題のあるシステムをリリースしてしまうリスクが高くなる。
次のメソッドをみて欲しい。
void e2j(String english){
return "我輩は猫である";
}
このメソッドの機能は本来和訳であるが入力値を
I am a cat.
とせねば正しい結果は得られない。まさにこのメソッドにはバグがあると云える。だがこのバグはテストでの捕捉が難しい。入力値を
I am a cat.
とすればテストOKとなってしまうからだ。OKとなればもうその同類のテストはやらない。バグは二度と見つからなくなる。
この問題の解決方法はレビューである。レビューをすれば簡単にバグを捕捉できる。
検証とは本来、テストとレビューのダブルチェックにより行うべきである。ダブルチェックをしなければバグはなくならない。だがコストがかかるという理由からダブルチェックをやらないというシステム開発もある。もちろんそんな開発は論外である。