give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

予備知識が無いと理解しづらかったり、意味づけが人によって異なったりするようなプログラム設計は避けるべき、というお話 #sg_study

はじめに

2014年7月8日に、ソニックガーデンのweb勉強会であるSonicGarden Studyで、「CodeIQ ベストコード発表会 ~最もエレガントにカラオケマシン問題を解いた挑戦者は誰だ!?~」を放送したのは前回のエントリでお伝えしたとおりです。


放送後のアンケートでちょっと気になる質問を頂いたので、今回はその質問に対して僕の考えを回答したいと思います。


放送後のアンケートでいただいた質問の内容

以下が視聴者の方からいただいた質問です。(了承をもらって公開しています)

CodeIQからのフィードバックで頂いた内容で 「音楽では♯だけでなく♭もあるので...このロジックだと辛いかも...」 と指摘を頂きました。
はい、ごもっともと納得しています。


で、本Studyで、YAGNIというキーワードありで (仕様上、♭はなかったと認識、よって♭変換不要の前提) 模範コードには、♭への拡張性があったのか?なかったのか?が よくわかりませんでした。


ruby自体よくわかってないので、優秀+模範コードもじっくりみないと理解できないレベルなので大変恐縮ですが、 拡張性があったの?なかったの?だけでも何らかの形でご教授いただけるとうれしいです。

 

質問に関する補足説明

質問者の方だけでなく他にも何名かいたのですが、カラオケマシン問題でシャープ(#)付きの音符は内部的に小文字として扱う、という方がいました。


たとえば、"D F# A" は "D f A" として扱う、といった感じです。


これに対して、僕は「音楽的にはシャープ(#)だけなくフラット(♭)も出てくるので、このロジックだとフラットが表現できないですね」みたいなコメントを返していました。


一方で、SonicGarden Studyの放送では「YAGNIなシンプル設計が良い」、つまり「今提示されている仕様にだけ対応できれば良い。『xxxな場合にも対応できるようにしました』みたいに、柔軟性を重視しすぎてプログラムを複雑にするのは望ましくない」という話をしました。


また、良いコードの特性として「拡張性が高いこと」も挙げました。


最後に、僕の模範解答はこんなコードになっています。

class KaraokeMachine
  SCALE = %w(C C# D D# E F F# G G# A A# B).freeze

  def initialize(melody)
    @melody = melody
  end

  def transpose(amount)
    converter = [SCALE, SCALE.rotate(amount)].transpose.to_h
    @melody.gsub(/[A-G]#?/, converter)
  end
end

 

そう言われてみると、YAGNIを重視すべきなのは確かだし、模範解答の拡張性についてもハッキリとYES/NOを出せないかも

なるほど、

  • フラットが出てきたら破綻するといっても、YAGNIでいいんでしょ?
  • 拡張性が大事っていうけど、模範解答はそんなに拡張性が高いの?

という質問者さんの疑問は確かにもっともです。


そう言われてみると、ちょっと説明の仕方があまり良くなかったかな~という気がしてきました。


YAGNIでいいんでしょ、というのは確かにその通りです。
なので「フラットが出たら破綻しますよね」という指摘はちょっと理由付けが弱くなります。


また、模範解答にはフラットにすぐ対応できるような拡張性があるのか?と聞かれると、YESともNOとも答えられません。
そもそも、今回のカラオケマシン問題では「フラットを使う場合」を全く想定していないので、それがどんな仕様になるのかは出題者である僕ですらわかっていません。


「"/[A-G]#?/" になっている正規表現を、"/[A-G][#b]?/" みたいにすれば、"Bb" みたいな音符も抽出できるよな~」とは思いますが、そこからどうするのかはわかりません。(そもそも仕様が決まってないですし)


とはいえ、それでもやはりシャープを小文字にするのはあまり好ましくないと僕は考えます。
その理由を以下に述べます。


それでもシャープを小文字にするのはあまり好ましくない、と思う理由

まず、自己弁護するわけではありませんが、模範解答は「"/[A-G][#b]?/" みたいにすれば、"Bb" みたいな音符も抽出できる」というぐらいの拡張性はあります。


しかし、シャープを小文字に変換するロジックだと、フラット付きの音符を一文字で表現する手段がありません。
なので、フラットが出てくる仕様に対応するとなれば、根っこからロジックを書き換える必要があります。


まあ、音楽的なことを言うと、シャープとフラットは基本的に置換可能なので、"D Gb A (レソbラ)" は "D F# A (レファ#ラ)"と書いても全く同じメロディになります。


f:id:JunichiIto:20180715104609g:plain
http://www.convivace.jp/kenban_all.gif


だから、フラット付きの音符も全部シャープ付きの音符に置き換えれば、引き続き同じロジックが使えるかもしれません。
もしくはフラット付きの音符は全角文字として表す、というルールにすれば・・・と、バリエーションは他にも考えられるかもしれませんが、「フラットが出てきた場合の仕様」が決まってない以上、このあたりの話を深く議論してもあまり意味がないです。


それよりも、一番の問題は何かというと、「小文字はシャープを表す」というルールがあまりにも独自ルールである点です。


予備知識が無いと理解しづらかったり、意味づけが人によって異なったりするような設計は避けるべき

つまり、このロジックを考えた人は「"D f A" は "D F# A"だ」とわかりますが、予備知識のない人は「D f A」という文字列を見ても「D F# A」を表していると解釈できません。


また実際のプログラムでは「D f A」がコードとして表現されることはありません。
ロジックとしては「シャープを小文字に変換する処理 → キーの上げ下げ → 小文字をシャープに戻す」というようなコードが書かれると思います。
そのコードを読む人が「小文字はシャープ」というルールを知らなかった場合、「はて、なんでここで小文字を特殊に処理してるんだろう??」と疑問を抱きます。


少し話はそれますが、挑戦者の方の中には「変換前のメロディを小文字として表す」というロジックを使っている方もいました。
「C E G」をまず「c e g」にして、それを2音上げると「D F# A」になる、というような具合です。


この人は小文字に対して「小文字は変換前の音符である」という意味を持たせたんですね。
文字列から小文字が全部なくなったら、メロディの変換が完了した、という印になります。


このように「小文字であること」に何か特別な意味を持たせようとすると、付与される意味が人によって異なります。


ですから当然、コードを読む人はその意味を予備知識として持っておかなければロジックを理解できません。
コードを改造するときも、予備知識のない人が改造すると思わぬ不具合を埋め込んでしまう可能性があります。


こうした問題があることから、やはり「シャープを小文字として表す」というロジックはあまり好ましくないと思うのです。
つまり、「小文字」は小文字であって、それ以上の意味を持たせるのは避けるべき、ということになります。


事前に知っておかないと理解が困難になる予備知識や前提条件のことを「コンテキスト」と呼びます。
プログラムの中ではできるだけコンテキストに依存しない設計やロジックにすることが望ましいです。


もちろん、プログラムの規模が大きくなってくるとある程度のコンテキストが発生することは避けられないかもしれませんが、それでも外からやって来た人が「まさかそんなロジックになっていたとは!!」と驚くような「サプライズ設計」を埋め込まないように気をつけましょう。


そこでコードレビューの出番ですよ!!

そうは言っても、自分でプログラムを書いているとその設計が「サプライズ設計」なのかどうか客観的に判断するのは難しいです。
コードは書く人は誰でも「このコードがベストだ」と思いながらコードを書いてるはずでしょうしね。


そんな思い込みをいい意味で打ち砕いてくれるのがコードレビューです。
それも自分よりも優秀な「優れたプログラマによってレビュー」してもらえることが望ましいです。


ちょうどいいいことに、次回のSonicGarden Studyはまさにそんなテーマになっています!


8月のSonicGarden Studyはこんな内容です
タイトル
「いつまでクソコードを 書き続けるの? ~デキるプログラマだけが知っている コードレビュー7つの秘訣~」
内容
優れたプログラマだけが優れたソースコードを書くことができます。では優れたプログラマになるにはどうすれば良いでしょうか。
自分の書いたコードを、優れたプログラマに指摘してもらうことが一番の近道です。それがコードレビューです。
たった一人でコードレビューも受けずに、ただ書き続けてもクソコードはクソコードなのです。
この放送では、良いコードが書けるプログラマになるための、コードレビューを上手に実践するための秘訣を話します。
講師
西見公宏(@mah_lab)
放送日時
8/11(月) 19:30 - 20:30
告知ページ
http://sonicgarden.doorkeeper.jp/events/13229

f:id:JunichiIto:20140711104726p:plain


前回の「CodeIQ ベストコード発表会」と同じく、Ustreamを使ったweb勉強会となっているので、誰でもどこからでも参加可能です。
みなさんぜひ申し込んでくださいね!!


まとめ

というわけで、今回はCode IQに出題した「カラオケマシン問題」をテーマにして、「良いコード」の条件についてお話ししてみました。


いかがだったでしょうか?
みなさんも自分のチームや組織で、こういったテーマについて議論するとメンバーの「良いコードに対する意識」が高まるかもしれません。


また、「良いコードかどうかを客観的に判断してもらうためのコードレビュー」も非常に重要です。
自分の書いたコードは必ず誰か(できれば優秀なプログラマ)に読んでもらうようにしましょうね!


参考書籍

良いコードや良い設計に対する理解を深めるためには、まずこういった技術書で勉強しておくことも大切ですね。

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

  • 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2012/06/23
  • メディア: 単行本(ソフトカバー)
  • 購入: 68人 クリック: 1,802回
  • この商品を含むブログ (140件) を見る


あわせて読みたい

昨日公開したエントリです。「CodeIQ ベストコード発表会」の内容を一通りまとめています。

「CodeIQ ベストコード発表会 ~最もエレガントにカラオケマシン問題を解いた挑戦者は誰だ!?~」を放送しました #sg_study - give IT a try