「プログラミングが好きな学生のためのリーダブルコード勉強会」で開発する プログラムについて説明します。
開発するプログラムは「レシピ管理プログラム」です。
この勉強会は将来的に見て「リーダブルコード力のレベルアップをすること」 が目的であり、今日だけのレベルアップは目的ではありません。そのために 「リーダブルコードを発見できるようになること」を目指しますが、「リーダ ブルコードのテクニックをたくさん覚えること」や「難しいプログラムでも実 装できるようになること」、「速く実装できるようになること」は目指すこと ではありません。そのため、次の点に注意して開発するプログラムの仕様を用 意しました。
- 言語の基本的な機能のみで実現できる。
- ライブラリーを知っていることや使えることは重要ではない。
- 難しいロジックを必要としない。
- 効率のよいアルゴリズムを考えることは重要ではない。
- 段階的に改良していく。
- 書き捨てのプログラムではなく継続的に開発するプログラムであること は重要である。
- すべての仕様を実装しなくてもよい。
- 今回の実装時間は150分で仕様は13個ある。平均して11分で1つ実装する とすべての仕様を実装できるが、「速く実装できるようになること」は 目的ではないので、すべての仕様を実装することは重要ではない。
開発するコードは「今の自分で最高にリーダブルなコード」にしてください。
そのとき、「どうしてこのコードはリーダブルと言えるのか」の理由も考えて
ください。例えば、「 do_something
では何かをするのはわかるけど何をす
るのかわからないが、 open_file
ならファイルを開くということがすぐわか
るのでリーダブルだ」といった具合です。
理由を考えるのは、理由をつけられるくらい考えていれば応用が効くからです。 前述の例の理由からは「何をするのかわかる名前がよい」という基準を見つけ られます。この基準を使えば、違うコードでもリーダブルなコードを書けます。
1つ、リーダブルコードを書くためのヒントを伝えます。それは、「書く時に読 む人の視点を持つこと」です。
リーダブルコードとはどのようなコードだと思いますか?読む人がリーダブル だと思ったら、それはその人にとってリーダブルコードです。読む人にとって 前提知識が違うので、より多くの人がリーダブルだと思うコードもありますし、 同じ開発チームの人たちだけがリーダブルだと思うコードもあります。どちら を選ぶかは「読む人」をどのように設定したかに依存します。必ずしも「より 多くの人」を選ぶことが妥当なわけではありません。例えば…、と書き始める とヒントではなくなってしまいますね。ここで止めておくことにします。
書く時は「どんな人が読むコードか」を意識してみてください。それが、読む 人の視点を持つことにつながります。
プログラムを1から開発します。後述する仕様を実現するようにプログラムを開 発します。仕様は複数あり、仕様1を実装したら、仕様2を実装する、というよ うに、同じプログラムを改良しながら仕様を実現していきます。
すべての仕様をいかに速く実装することが目的では ありません 。プログ ラムを開発しながら「リーダブルコードの考え方や姿勢を身につけること」が 目的です。「1番速く実装したけどリーダブルコードについてはなにも得られる ことがなかった」というケースはこの勉強会では失敗ケースです。目的を見失 わないようにしましょう。
1つ仕様を実装する毎にGitHubにcommit + pushしてください。
例:
- 仕様1を実装
- commit + push
- 仕様2を実装
- commit + push
- ...
仕様を実装した状態でcommit + pushしていれば、1つの仕様を実装する間に何 回commit/pushしても構いません。
例:
- 仕様1を実装開始
- 仕様1を30%実装
- commit
- 仕様1を60%実装
- commit + push
- 仕様1を90%実装
- commit
- 仕様1を100%実装
- commit + push
- ...
コミットメッセージにはどの仕様を実装しているかがわかるマークを入れてく
ださい。具体的には、コミットメッセージの最初に spec${仕様番号}:
と入
れてください。
仕様1を実装しているときのコミット例:
% git commit -m "spec1: add a sample script"
% git commit -m "spec1: 並び替え機能を追加"
仕様3を実装しているときのコミット例:
% git commit -m "spec3: fix a typo"
% git commit -m "spec3: 異常値のときのテストを追加"
これは、どの仕様を実装しているかをわかりやすくするためです。
それぞれの仕様には次の2点を明記しています。仕様を実装した後は、各自、こ れらを使って仕様を満たしているかどうかを確認してください。不安な場合は メンターに質問したり、確認をお願いしてください。
- 入力
- 期待する結果
レシピのタイトルとして「オムライス」と出力するプログラムを新しく作って ください。
なし
プログラムを実行したらなんらかの形で「オムライス」と出力してください。 コンソールに出力しても構いませんし、ファイルに出力しても、ウィンドウを 作ってそこに出力しても、Webブラウザーに表示させても構いません。なんらか の形で「オムライス」と出力してください。
recipe.sh:
#!/bin/sh
echo "オムライス"
実行:
% chmod +x recipe.sh
% ./recipe.sh
オムライス
同じグループの人があなたが書いたプログラムを実行できるように、実行手順 を書いたREADMEを用意してください。
フォーマットはなんでも構いませんが、適切な拡張子を付けてください。例え ば、特にフォーマットが決まっていないテキストであれば.txt、Markdownであ れば.mdなどといった具合です。
なし
READMEに書かれた手順に従って作業すれば、このプログラムが仕様1を満たして いることを確認できるようにしてください。
今はプログラム中に「オムライス」のレシピが埋め込まれているはずです。そ のレシピ情報をデータとして別のファイルに保存し、プログラムとは分離して ください。
ファイルのフォーマットは問いません。
どのようにファイルを指定するかも問いません。
どのようなフォーマットにしたか、どのようにファイルに指定するかはREADME に記述してください。
- データファイル
- ファイル名は任意。例: recipe-data.txt
- 中身は「オムライス」というレシピ情報1つだけ。
- データファイルはリポジトリーに入れること
データファイルは、例えばこういう内容:
% cat recipe-data.txt
オムライス
%
プログラムを実行したらなんらかの方法でデータファイルを読み込み、データ ファイル内にある「オムライス」というレシピ情報をロードしてください。そ して、そのレシピ情報を出力してください。
実行例:
% ./recipe.sh recipe-data.txt
オムライス
%
READMEを読めば、グループの他の人でもファイルを作成してプログラムにレシ ピ情報をロードできるようにしてください。
今のデータファイルは「オムライス」しかレシピ情報が入っていません。これ を、次の3つのレシピ情報を登録できるようにします。
- オムライス
- 親子丼
- 杏仁豆腐
また、登録されているすべてのレシピを出力してください。
レシピ情報のフォーマットは問いません。
どのようなフォーマットにしたかはREADMEに記述してください。
- データファイル
- ファイル名は任意。例: recipe-data.txt
- 中身は次の3つのレシピ情報。
- オムライス
- 親子丼
- 杏仁豆腐
データファイルは、例えばこういう内容です。
% cat recipe-data.txt
オムライス
親子丼
杏仁豆腐
%
プログラムを実行したらなんらかの方法でデータファイルを読み込み、データ ファイル内にある3つのレシピ情報をロードしてください。そして、そのレシピ 情報をすべて出力してください。
実行例:
% ./recipe.sh recipe-data.txt
オムライス
親子丼
杏仁豆腐
%
READMEを読めば、グループの他の人でもファイルを作成できるようにしてくだ さい。
今のプログラムは同じ料理のレシピを複数登録すると区別できません。登録さ れたレシピにプログラム内で自動でID(レシピを一意に識別できる識別子)を 振ってレシピを区別できるようにしてください。IDは数値でも文字列でも一意 であればなんでも構いません。
出力するときはIDも出力してください。
- データファイル
- 中身は前の仕様と同じ次の3つのレシピ情報。
- オムライス
- 親子丼
- 杏仁豆腐
- 中身は前の仕様と同じ次の3つのレシピ情報。
データファイルは、例えばこういう内容です。
% cat recipe-data.txt
オムライス
親子丼
杏仁豆腐
%
プログラムを実行したら読み込んだレシピ情報になんらかの方法でIDを振って ください。そして、そのレシピ情報をID付きですべて出力してください。
実行例:
% ./recipe.sh recipe-data.txt
1: オムライス
2: 親子丼
3: 杏仁豆腐
%
今のプログラムは読み込んだレシピをすべて表示します。IDを指定した場合は 指定したIDのレシピだけを表示してください。
IDの指定方法は問いません。
IDの指定方法はREADMEに記述してください。
指定されるIDは必ず存在するIDと仮定してください。
IDを指定されなかった場合の挙動は変えないでください。
- データファイル
- 中身は前の仕様と同じ次の3つのレシピ情報。
- オムライス
- 親子丼
- 杏仁豆腐
- 中身は前の仕様と同じ次の3つのレシピ情報。
- ID
プログラムを実行したらレシピ情報を読み込んでください。その後、指定され たIDのレシピのみ表示してください。
IDを指定されなかった場合はすべてのレシピを表示してください。
実行例:
% ./recipe.sh recipe-data.txt 2
2: 親子丼
% ./recipe.sh recipe-data.txt
1: オムライス
2: 親子丼
3: 杏仁豆腐
%
READMEを読めば、グループの他の人でもIDを指定できるようにしてください。
今のプログラムはレシピの料理名しか情報を持っていません。それぞれのレシ ピにクックパッドのURLを関連付けて作り方がわかるようにしてください。
出力時は料理名とURLの両方を表示してください。
データファイルのフォーマットは問いません。
どのようなフォーマットにしたかはREADMEに記述してください。
- データファイル
- ファイル名は任意。例: recipe-data.txt
- 中身は次の3つのレシピ情報。
- オムライス
- 親子丼
- 杏仁豆腐
- それぞれクックパッドから料理のレシピを探してきてURLを関連付けること
データファイルは、例えばこういう内容です。
% cat recipe-data.txt
オムライス http://cookpad.com/recipe/2653946
親子丼 http://cookpad.com/recipe/2657882
杏仁豆腐 http://cookpad.com/recipe/2654398
%
プログラムを実行したらクックパッドのURL入りレシピ情報を読み込み、読み込 んだレシピ情報を表示します。
実行例:
% ./recipe.sh recipe-data.txt
1: オムライス http://cookpad.com/recipe/2653946
2: 親子丼 http://cookpad.com/recipe/2657882
3: 杏仁豆腐 http://cookpad.com/recipe/2654398
%
READMEを読めば、グループの他の人でもURLを関連付けられるようにしてくださ い。
今のプログラムはレシピ情報の集まりをロードして出力するだけです。これに、 だれが集めたレシピ情報かを指定できるようにしてください。「だれ」のこと を「ユーザー名」と呼ぶことにします。出力するときはユーザー名も出力して ください。
ユーザー名に使える文字は次の通りとします。
- 小文字のアルファベット
- 数字
- ハイフン
- アンダーバー
ユーザー名は長くても10文字とします。
この条件を満たさないユーザー名が指定されることはないと仮定して構いませ ん。
ユーザー名の指定方法は問いません。
どのような指定方法にしたかはREADMEに記述してください。
ユーザー名の出力方法は問いません。
- データファイル
- ファイル名は任意。例: recipe-data.txt
- 中身は次の3つのレシピ情報。
- オムライス
- 親子丼
- 杏仁豆腐
- それぞれクックパッドから料理のレシピを探してきてURLを関連付けること
- レシピを集めているユーザー名
データファイルは、例えばこういう内容です。
% cat recipe-data.txt
オムライス http://cookpad.com/recipe/2653946
親子丼 http://cookpad.com/recipe/2657882
杏仁豆腐 http://cookpad.com/recipe/2654398
%
プログラムを実行したら、レシピ情報とそのレシピ情報を集めているユーザー 名を受け取り、ユーザー名と読み込んだレシピ情報を表示します。IDを指定し たら指定したレシピだけ表示する機能は壊さないでください。
実行例:
% ./recipe.sh kou recipe-data.txt
ユーザー名: kou
1: オムライス http://cookpad.com/recipe/2653946
2: 親子丼 http://cookpad.com/recipe/2657882
3: 杏仁豆腐 http://cookpad.com/recipe/2654398
% ./recipe.sh kou recipe-data.txt 2
ユーザー名: kou
2: 親子丼 http://cookpad.com/recipe/2657882
%
READMEを読めば、グループの他の人でもユーザー名を指定できるようにしてく ださい。
今のプログラムは1人のユーザーのレシピ情報だけを扱えますが、これを4人の ユーザーのレシピ情報も扱えるようにしてください。
出力するときはユーザー毎にレシピを出力してください。
ユーザー名とそのユーザーのレシピ情報の指定方法は問いません。
どのような指定方法にしたかはREADMEに記述してください。
ユーザー名は重複するかもしれません。
- ユーザー名とデータファイルのペアを4つ
- データファイル内のレシピ数は3つ
- 1つはこれまで使ってきたデータファイルを使うこと
- 残りの3つはこれまで使ってきたデータファイルを参考に新しく作ること
- 異なるユーザーで同じレシピ情報を持っているかもしれない
- 例: ユーザー1とユーザー2のどちらにも「オムライス」がある
- すべてのレシピは異なるIDを持つこと
- すべてのデータファイルはリポジトリーに入れること
データファイルは、例えばこういう内容です。
% cat recipe-data1.txt
オムライス http://cookpad.com/recipe/2653946
親子丼 http://cookpad.com/recipe/2657882
杏仁豆腐 http://cookpad.com/recipe/2654398
% cat recipe-data2.txt
オムライス http://cookpad.com/recipe/2653779
鶏の唐揚げ http://cookpad.com/recipe/2660337
カレー http://cookpad.com/recipe/2661962
% cat recipe-data3.txt
トマトサラダ http://cookpad.com/recipe/2662101
生ハムサラダ http://cookpad.com/recipe/2661792
和風サラダ http://cookpad.com/recipe/279208
% cat recipe-data4.txt
チョコケーキ http://cookpad.com/recipe/2661922
スイートポテト http://cookpad.com/recipe/2639428
杏仁豆腐 http://cookpad.com/recipe/2565701
%
プログラムを実行したら、レシピ情報とそのレシピ情報を集めているユーザー 名を受け取り、ユーザー名と読み込んだレシピ情報を表示します。IDを指定し たら指定したレシピだけ表示する機能を壊さないでください。
実行例:
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt
ユーザー名: kou
1: オムライス http://cookpad.com/recipe/2653946
2: 親子丼 http://cookpad.com/recipe/2657882
3: 杏仁豆腐 http://cookpad.com/recipe/2654398
ユーザー名: piro
4: オムライス http://cookpad.com/recipe/2653779
5: 鶏の唐揚げ http://cookpad.com/recipe/2660337
6: カレー http://cookpad.com/recipe/2661962
ユーザー名: okkez
7: トマトサラダ http://cookpad.com/recipe/2662101
8: 生ハムサラダ http://cookpad.com/recipe/2661792
9: 和風サラダ http://cookpad.com/recipe/279208
ユーザー名: kou
10: チョコケーキ http://cookpad.com/recipe/2661922
11: スイートポテト http://cookpad.com/recipe/2639428
12: 杏仁豆腐 http://cookpad.com/recipe/2565701
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt sunaot recipe-data4.txt 5
ユーザー名: kou
ユーザー名: piro
5: 鶏の唐揚げ http://cookpad.com/recipe/2660337
ユーザー名: okkez
ユーザー名: kou
%
READMEを読めば、グループの他の人でもユーザー名と対応するデータファイル を指定できるようにしてください。
今のプログラムは同じユーザー名だと区別できません。指定されたユーザーに プログラム内で自動でID(ユーザーを一意に識別できる識別子)を振ってユー ザーを区別できるようにしてください。IDは数値でも文字列でも一意であれば なんでも構いません。
出力するときはIDも出力すること。
- ユーザー名とデータファイルのペアを4つ
- データファイル内のレシピ数は3つ
- 1つはこれまで使ってきたデータファイルを使うこと
- 残りの3つはこれまで使ってきたデータファイルを参考に新しく作ること
- 異なるユーザーで同じレシピ情報を持っているかもしれない
- 例: ユーザー1とユーザー2のどちらにも「オムライス」がある
- すべてのレシピは異なるIDを持つこと
データファイルは、例えばこういう内容:
% cat recipe-data1.txt
オムライス http://cookpad.com/recipe/2653946
親子丼 http://cookpad.com/recipe/2657882
杏仁豆腐 http://cookpad.com/recipe/2654398
% cat recipe-data2.txt
オムライス http://cookpad.com/recipe/2653779
鶏の唐揚げ http://cookpad.com/recipe/2660337
カレー http://cookpad.com/recipe/2661962
% cat recipe-data3.txt
トマトサラダ http://cookpad.com/recipe/2662101
生ハムサラダ http://cookpad.com/recipe/2661792
和風サラダ http://cookpad.com/recipe/279208
% cat recipe-data4.txt
チョコケーキ http://cookpad.com/recipe/2661922
スイートポテト http://cookpad.com/recipe/2639428
杏仁豆腐 http://cookpad.com/recipe/2565701
%
プログラムを実行したら読み込んだユーザーになんらかの方法でIDを振る。そ して、そのユーザー名をID付きですべて出力する。
実行例:
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt
ユーザー: 1: kou
1: オムライス http://cookpad.com/recipe/2653946
2: 親子丼 http://cookpad.com/recipe/2657882
3: 杏仁豆腐 http://cookpad.com/recipe/2654398
ユーザー: 2: piro
4: オムライス http://cookpad.com/recipe/2653779
5: 鶏の唐揚げ http://cookpad.com/recipe/2660337
6: カレー http://cookpad.com/recipe/2661962
ユーザー: 3: okkez
7: トマトサラダ http://cookpad.com/recipe/2662101
8: 生ハムサラダ http://cookpad.com/recipe/2661792
9: 和風サラダ http://cookpad.com/recipe/279208
ユーザー: 4: kou
10: チョコケーキ http://cookpad.com/recipe/2661922
11: スイートポテト http://cookpad.com/recipe/2639428
12: 杏仁豆腐 http://cookpad.com/recipe/2565701
今のプログラムはすべてのユーザーを表示します。IDを指定した場合は指定し たIDのユーザーだけを表示してください。
IDの指定方法は問いません。
IDの指定方法はREADMEに記述してください。
指定されるIDは必ず存在するIDと仮定して構いません。
IDを指定されなかった場合の挙動は変えないでください。
ユーザーIDとレシピIDを指定した場合は該当ユーザーの該当レシピ情報だけ表 示してください。レシピIDは指定したユーザーが持っているレシピのIDである と仮定して構いません。
- ユーザー名とデータファイルのペアを4つ
- データファイル内のレシピ数は3つ
- 1つはこれまで使ってきたデータファイルを使うこと
- 残りの3つはこれまで使ってきたデータファイルを参考に新しく作ること
- 異なるユーザーで同じレシピ情報を持っているかもしれない
- 例: ユーザー1とユーザー2のどちらにも「オムライス」がある
- すべてのレシピは異なるIDを持つこと
- ユーザーID
データファイルは、例えばこういう内容:
% cat recipe-data1.txt
オムライス http://cookpad.com/recipe/2653946
親子丼 http://cookpad.com/recipe/2657882
杏仁豆腐 http://cookpad.com/recipe/2654398
% cat recipe-data2.txt
オムライス http://cookpad.com/recipe/2653779
鶏の唐揚げ http://cookpad.com/recipe/2660337
カレー http://cookpad.com/recipe/2661962
% cat recipe-data3.txt
トマトサラダ http://cookpad.com/recipe/2662101
生ハムサラダ http://cookpad.com/recipe/2661792
和風サラダ http://cookpad.com/recipe/279208
% cat recipe-data4.txt
チョコケーキ http://cookpad.com/recipe/2661922
スイートポテト http://cookpad.com/recipe/2639428
杏仁豆腐 http://cookpad.com/recipe/2565701
%
プログラムを実行したらユーザー名と対応するレシピ情報を読み込んでくださ い。その後、指定されたIDのユーザーのみ表示してください。
実行例:
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt 4
ユーザー: 4: kou
10: チョコケーキ http://cookpad.com/recipe/2661922
11: スイートポテト http://cookpad.com/recipe/2639428
12: 杏仁豆腐 http://cookpad.com/recipe/2565701
レシピIDも指定されたら該当ユーザーの該当レシピ情報だけ表示してください。
実行例:
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt 4 11
ユーザー: 4: kou
11: スイートポテト http://cookpad.com/recipe/2639428
レシピIDだけ指定したときはこれまでと同じ挙動にしてください。次の例は、 ユーザーIDに-1を指定したらユーザーIDは指定なしという仕様にしています。
実行例:
% ./recipe.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt -1 11
ユーザー: 1: kou
ユーザー: 2: piro
ユーザー: 3: okkez
ユーザー: 4: kou
11: スイートポテト http://cookpad.com/recipe/2639428
READMEを読めば、グループの他の人でもIDを指定できるようにしてください。
ユーザー・レシピ管理部分をライブラリーとして切り出し、複数のプログラム から再利用できるようにしてください。これまでの挙動をするプログラムは、 この切り出したライブラリーを使うようにしてください。挙動は変えないでく ださい。
次の入出力部分はライブラリーではなく、プログラム側に入れてください。
- このプログラムの利用者からユーザー名やレシピ情報などの入力を受け取る部分
- データファイルを読み込む部分
- 結果を出力する部分
プログラムをビルドする方法や実行方法が変わった場合はREADMEを更新してください。
仕様11と同じ。
仕様11と同じ。
READMEを読めば、グループの他の人もプログラムを実行できるようにしてくだ さい。
ライブラリーを使用して結果をCSVで出力する別のプログラムを作ってください。
出力フィールドは左から順に次のとおりにしてください。
- ユーザーID
- ユーザー名
- レシピID
- レシピ名
- レシピURL
READMEに実行方法を追記してください。
仕様11と同じ。
プログラムを実行したらユーザー名と対応するレシピ情報を読み込んでくださ い。その後、CSV形式でユーザー名と対応するレシピ情報を表示してください。
実行例:
% ./recipe-csv.sh kou recipe-data1.txt piro recipe-data2.txt okkez recipe-data3.txt kou recipe-data4.txt
1,kou,1,オムライス,http://cookpad.com/recipe/2653946
1,kou,2,親子丼,http://cookpad.com/recipe/2657882
1,kou,3,杏仁豆腐,http://cookpad.com/recipe/2654398
2,piro,4,オムライス,http://cookpad.com/recipe/2653779
2,piro,5,鶏の唐揚げ,http://cookpad.com/recipe/2660337
2,piro,6,カレー,http://cookpad.com/recipe/2661962
3,okkez,7,トマトサラダ,http://cookpad.com/recipe/2662101
3,okkez,8,生ハムサラダ,http://cookpad.com/recipe/2661792
3,okkez,9,和風サラダ,http://cookpad.com/recipe/279208
4,kou,10,チョコケーキ,http://cookpad.com/recipe/2661922
4,kou,11,スイートポテト,http://cookpad.com/recipe/2639428
4,kou,12,杏仁豆腐,http://cookpad.com/recipe/2565701
READMEを読めば、グループの他の人もプログラムを実行できるようにしてくだ さい。
メンターに仕様を聞いてください。