読みやすいコードとワーキングメモリ
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)
- 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/06/23
- メディア: 単行本(ソフトカバー)
- 購入: 68人 クリック: 1,802回
- この商品を含むブログ (135件) を見る
3年ばかり前に『リーダブルコード』という本を図書館で借りて読んだ(ので詳細な内容を忘れてしまった)のですが、
こういった本が出版されるほどに、エンジニアは読みやすいコードを求めているのだと感じます。
コードの品質については様々な要素があります。その中に、可読性というものも入るでしょう。
可読性の高さは、コードの理解を容易にします。
コードの理解が容易であれば、変更も加えやすく、拡張もしやすくなる可能性があります。
その可読性も、いくつかの要素から成り立っているようです。
- トークン単位 ... 命名は妥当か。値やメソッド名、クラス名から挙動などの想像がつくか。
- 式単位 ... プリミティブな命令の単位。どのような命令なのか、見てわかるか。
- 制御文、スコープ、メソッド単位 ... この文やスコープの意味するところは何なのかを、想像できるか。
- クラス、モジュール、ファイル単位 ... 理解しようという気になるか。
これらすべての根底には、読者にかかる認知的負荷を低くできるかどうか、という問題が横たわっています。
認知的負荷というのは、一時記憶(ワーキングメモリ)の圧迫と言い換えられるでしょう。
要は、覚えておけるかどうか、です。
人間の一時記憶は 7±2 個のことがら(チャンク)しか貯蔵できないと言われています(マジカルナンバー・セブン)。
コードを書いている本人ならともかく、読む人はたくさんのことがらを記憶できません。
しかし、記憶できることがら(チャンク)は、意味のある一かたまりであれば、ある程度記憶がしやすくなるのです。
例えば数字列。
097153754865
これを覚えろ、と言われてもなかなか難しいかもしれません。
数字一つひとつを記憶しようとしても、12個あるわけですから、7±2 を明らかに逸脱しています。
では、こちらは?
0971 - 5375 - 4865
まだ覚えやすいですね。
例えばこれが紙に書かれた電話番号だったとして、番号を打ち込むのが少しは楽になりそうです。
一つの塊は4桁ですから、塊ごとに記憶するのは容易。
その塊も3つですから、数字を一つずつ覚えこもうとするよりかははるかに楽になります。
コードについても同じことが言えます。
なるべく、単純化・抽象化した塊で捉えることで理解が楽になるわけです。
// 昔メンテナンスしていたプロダクトでこんな感じのコードを見たことがあります boolean ret; if (isSucceeded(results)) { if (results.targets.size() > 0) { ret = true; } else { ret = false; } } else { ret = false; } return ret; // 変数が減れば、頭に入れておくべきことが減ります。 // 行数や制御構文が減れば、「このまとまりが何をしているのか」を少ない労力で考えられます。 return isSucceeded(results) && (results.targets.size() > 0);
ある特定の操作をメソッドに抽出する、というのも有効な手段です。
// ナイーブに実装すると int index = -1; for (int i = 0; i < elements.size(); i++) { Element e = elements.get(i); if (e.id != target.id && e.name.equals(target.name)) { index = i; break; } } // 特定の操作をメソッドに置き換える -> 抽象化する ということです int index = indexOf(elements, () -> e.id != target.id && e.name.equals(target.name)); /* (ラムダ式部は、以下の省略形です) new Predicate<Element> { boolean test(Element e) { return e.id != target.id && e.name.equals(target.name); } } */ // 最終的な行数は増えますが、こちらのメソッドの実装は頭に入れなくても良いでしょう // 「特定の条件を満たす要素が格納されているインデックスを探し出す」という役割だけを覚えておけばいいのですから public boolean <T> indexOf(List<T> list, Predicate<T> predicate) { for (int i = 0; i < elements.size(); i++) { T t = list.get(i); if (predicate.test(t)) { return i; } return -1; }
単純なことを複雑に実現することは楽なのです。
なぜなら「実現する」という最低限がすでにできているのに、「単純化する」という+αに頭を使わなくて済むから。
書いた行数が増えれば、なんだか仕事した感じにもなりますし。
単純なことを単純に実現する。
できないことではないのですが、疲れるタスクです。
書いた行数が成果とされるような(Web系でそんな職場は存在しないと信じたいですが)ところだと、マイナスに働くかも。
ですが、書いた自分がやっておけば、今後のこされたコードに触る人全員の労力を削減できます。
また、それだけ考えることができる人だと周囲も認識してくれますし、それが自信をつけることにも繋がるでしょう。
読む人に苦労をかけないコードを残していきたいものですね。
モジュール設計のGood UI/ Bad UI
意図しない誤操作を誘発したり、意図する操作を実行することが困難だったりするUIのことを、私はBad UIと呼んだりしています。
一度プロダクトとしてBad UIが出来上がって、出荷もされてしまうと、使う側はプロダクトのUIを変えることができないので、どうにか利便性を向上させようと工夫をこらします。
で、ここがBad UIのBadたる所以ですが、利便性を上げようとしても、どうも頓珍漢な方向に人間を導くのです。
時々Twitterなどで見かける、説明書きのシールが所狭しと貼られたセブンカフェのコーヒーメーカーとかが顕著な例だと思います。
どうも人というのは、安易に言葉による説明を行いがちな気がします。
セブンカフェの例であれば、言葉で全てを表示しようとするから、上記写真のように愉快な悲惨なことになるのではないでしょうか。
ではBad UIの反対のGood UIはどんなものかと言いますと、私個人としてはレゴブロックが一番よいUIであると思っています。
いかにも組み合わさりそうな、凸部分に凹部分があります。
それらをを押し当てれば、想像通り、二つのブロックがくっつく。
そこに言葉はいらない訳です。見るか触るかすれば機能と使いかたを想像できます。
説明不要&期待通り。それがGood UIだと考えています。
さて、プログラミングについても同じことが言えるのではないでしょうか。
ある働きをするクラスやメソッドの入力値にかかる制約を、「ドキュメントコメントに書いているから」とそれで満足していませんか?
// Bad API /** * 日時から曜日を算出します。 * * @param year 年。正の整数 * @param month 月。0-11の値、1月は0、12月は11 * @param day 日。1以上31以下の整数 */ public String nameOfDayOfWeekFromDate(int year, int month, int day) { ... } /** * 曜日が休日かどうか判断します。 * * @param dayOfWeek 曜日名。"mon", "tue" などの省略形 */ public boolean isHoliday(String dayOfWeek) { ... } ... // 書いた人以外の人が読んだらどう思うでしょうか。 // 引数に数値ならなんでも入っちゃうけど大丈夫? // 10月なんだろうけれど、1を引いている意図は? // 戻り値は一体どんな形式? String dow = nameOfDayOfWeekFromDate(2016, 10 - 1, 20); // 引数には曜日入れるっぽいけれど、どんな文字列入れていいの? if (isHoliday(dow)) { ... }
あるいは、レゴブロックのように「組み合わさるからOK」というインターフェースにはできないでしょうか。
// Good API /** * 曜日を表現します。 */ public enum DayOfWeek { MONDAY, TUESDAY, ... } /** * 日時から曜日を得ます。 * * @param date 日付 */ public DayOfWeek dayOfWeekFromDate(Date date){ ... } /** * 曜日が休日かどうか判断します。 * * @param dayOfWeek 曜日 */ public boolean isHoliday(DayOfWeek dayOfWeek) { ... } // 引数はDateをとる…日付が必要なんだな。考えることが少なくて良い。 // 戻り値は曜日を表すわけだな。表現形式とか気にしなくていい。 DayOfWeek dow = dayOfWeekFromDate(new Date()); // DayOfWeekオブジェクトしかはいらないから、これを入れよう。おっ、ちゃんと動くぞ! if (isHoliday(dow)) { ... }
レゴブロックの凹凸に相当するもの。
型です。
intやStringなど、汎用的すぎる型で必要なデータを取りそろえようと思ったら、制約条件をコメントに書くしかない、使う人に読んで理解して記憶して気をつけておいてもらわないといけない。
すなわち、使う側の人間に認知的負荷をかけなければいけない、ということになります。
そこで、もう少し表現を特殊化した型を用意してはいかがでしょう?
「この引数にこのメソッドの戻り値が当てはまるから入れて見る」という、ほとんど考えなくてもできるような作業でも、その結果として適切に動く可能性を飛躍的に向上することができるのです。
しかもチェックするのはコンパイラ。わざわざ人の力を借りる必要はありません。
全てが型でどうにかなるわけではないですが、それでも使える範囲で使っていきたいものです。
最近ではKotlinやSwiftが型でnullableであることを表現できる(Optinal型)ようになっているので、null値を許容するのかしないのか、安全なのかそうでないのか、これも是非とも型で表現したいですね。してくださいマジで。
Androidオールスターズ2で発表してきました
硬めのお話は会社ブログで書いた通りです。
↓ よんでね
が、ブログの趣旨から外れるから会社ブログに書けなかったけれど誰かに伝えたいよもやま話があるので、つれづれと書いておきます。
自分が忘れないように。
いつの間にか今年が8ヶ月分終わっていたのでまとめる
技術的Tipsはqiitaに書く時代になったので、ブログの更新がすっかり抜け落ちておりました。
今年が始まってからいつの間にか半年以上経ってしまっていたので、なにをしていたのか振り返りたいと思います。
主に自分のため。
1月〜4月
昨年からYYC Androidアプリのリニューアルをしていました。
とにかく勉強と開発しかしてなかったです。
あと、年の初めにこんな目標を立てました。
が、どちらもほとんど手をつけておりません。
機械学習はTensorFlowを触ったくらい。DTMはMacにGaragebandを入れたくらい。
何もしていないとも言う。
5月〜6月
有難いことに勉強会に出る機会をいただいて、何度か発表させていただくなどしました。
わざわざ聞きに来てくださった方に何か新しいものを持ち帰ってもらおう、と考えられるようになりました。
あと、趣味と実益を兼ねる形で、仕事で使うライブラリを個人で開発してたりしました。
スターをいただけて嬉しかったです。
仕事ではひたすらRxJavaいじってたり、Kotlinいじってたり、Jenkinsおじさんになっていたりしました。
あ、あとErgoDox買いました。
人生がばら色になると評判の開運キーボード買った=͟͟͞͞(^o^=͟͟͞͞(^o^=͟͟͞͞)=͟͟͞=͟͟͞͞(^o^=͟͟͞͞(^o^=͟͟͞͞)=͟͟͞=͟͟͞ pic.twitter.com/D2IpiCD7tT
— 菊池紘 (@kikuchy) June 3, 2016
タッチタイピングできないくせに無刻印を買ってしまったので、仕事の作業効率がガタ落ちしました。
今は家に持って帰って、時々練習しています。
しかし確かにこれで仕事してた間は肩が凝らなかったです。
7月〜8月
Androidオールスターズで発表させていただきました。
こんなにも発表資料がなかなか出来上がらなくて頭を抱えたのは、大学院の研究発表のとき以来かもしれません。
仕事の方ではiOSの開発案件に携わったり(私が直接コードを書いている訳ではないのですが)、Android開発以外のやることが増えてきました。
多少Swiftの知識もついてきた気がします。
これから
個人のサイドプロジェクトで作りたいものがいろいろあるので、まずは早く帰ることを目指します。
勉強したい、さわりたいものは以下。
- React.js
- TensorFlow
- SpringBoot
- Scala
あと、技術ネタではないものをブログに書きつけて、とにかくアウトプットする習慣をつけたいと思います。
職業人としてのエンジニアの3類型
ポエムです。
知人から「人は金では動かない(ので人件費をどう使ったらいいのか悩んでいる)」という話を聞きました。
が、金で動くことももちろんあり得る訳です。
そもそも人間は何で動くかといえば、感情を動かされるもので動くのではないかと私は思っています。
そう仮定した上で、職業人として、社会でビジネスをしているエンジニアの感情を動かすものはなんだろうと考えたところ、
エンジニアという仕事をしている人には、主に3つの感情を動かすもののパターンがあるんじゃないか、と思ったのでした。
ゴール実現型
「海賊王に、俺はなる!」的な人。
人生を賭して実現することが定まっており、エンジニアリングはその手段、という人。
仕事をする主目的は、人生の目標実現。
感情を動かすものは、自分の理想に共感するものや人。
会社の事業内容にとてもこだわるか、自分で事業を立ち上げていたりする。
技術に明るい人もいれば、そうでない人もいる印象。
職人型
技術を極めることに楽しみを見出したり、技術力を認めてくれることに嬉しさを感じる人。
仕事をする主目的は、技術の勉強をすることか、技術力を発揮すること。
感情を動かすものは、好奇心や承認欲求。
自分の勉強になるか、自分の腕を振るえればなんでもいいので、事業内容などはさほど気にしないし、正直どうでもいいこともある。
勉強は欠かさないので、技術に明るい人が多い印象。
エンジニアらしいエンジニア。CTO業している人やマサカリストが多そうな気がする。
生活のため型
お金を稼ぎたい人。
仕事をする主目的は、生活や遊びの資金のため。
感情を動かすものは、報酬や費用対効果(労働での疲労に対してどれくらい稼げるか)。
たまたま人よりエンジニアリングができてお金を稼げるからエンジニアしている。
仕事は楽であることに越したことはない。
勉強する意欲は高くないので、技術に明るい人は少ない印象。
誰がどの型、ということはなく、誰しもどの要素も持っていて、それなりの割合でまじりあっていると思います。
が、その人が持っている一番強い要素がわかれば、だいたい「どんな人」という印象は外さないのでは。
例えばあるエンジニアを見つけて、この人を雇いたいと思ったとして、パターン別に以下のアプローチをすれば、雇える可能性が高くなるかもしれません。
- ゴール実現型 -> 事業のビジョンを語って共感を得る
- 職人型 -> 事業の中で勉強の機会があるスキルセットを提示して興味を引く、その人のスキルセットが欲しいことを説く
- 生活のため型 -> 短い勤務時間、高い報酬を提示する