こんにちは、kazuです。
先日、未経験ながら縁あって実務案件に参加させていただき、その折にお世話になった方から「宣言的に書く意識を持つと読みやすいコードが書けるよ」とアドバイスをいただきました。
今回は実際に「宣言的に書く」にあたり苦労したことや分かったことをまとめてみます。
PS:「宣言的に書く」のイメージについて、複数の例を提示してくださっている記事がありますので共有します。
https://qiita.com/Hiroyuki_OSAKI/items/f3f88ae535550e95389d
「宣言的に書く」とは
まずこの概念の理解に戸惑いました。
「宣言的に書く」とはズバリ「何をしたいか」をパッと見で分かるように書いていくイメージです。
これと対比されるのが「命令的に書く」というもので、「処理の手順(をどうやるのか)」が逐一見えるように書いていくイメージです。
当初、上の説明だと「わかりそうでわからない」モヤモヤした感じがしてスッキリしませんでした。
というのも、僕の中では「処理を書いた」時点で「それって命令的じゃないの?」というイメージがあったためです。
理解のポイント
ポイントは「頭の切り替え」でした。
- 「命令型」の記述は、コンピュータが理解する処理流れ
- 「宣言型」の記述は、人間が理解しやすい処理の流れ
つまり「人」にフォーカスしてコードを書いていくのが「宣言的に書く」ということで腹落ちしました。
というのも、この意識を持つということはつまり「人が読んで理解しやすいか」という質問でチェックできるからです。
なので、「宣言的に書けているか?」は「人がサラッと読んでわかるか?」に置き換えることができ、結果的にイメージ理解が深まりました。
これを図にまとめると以下のイメージです。 (処理例はざっくり適当です笑)
左の図だと「あ、野菜炒め作ってるぽいな」とパッと見わかりますが、右の図だとやっていることを1つずつ追っていかないと掴みづらいかと思います。
この図のポイントは「具体的な処理はメイン処理の外に出す」ことです。 つまり、具体的な処理はそれ用のクラスに処理を書き、呼び出すということです。
宣言的に書くメリット
ここまで「宣言的」のイメージを見てきましたが、そもそもこれをするメリットは何なのでしょうか。
メリットは例として以下のような要素が挙げられます。
- (人が読んで)一目で何をしているかわかりやすい
- コード量が減る
- (読み間違いからくるバグを防ぎやすくなり)保守性が向上する
- (パッと読みやすくなるので)開発スピードも上がる
やはり「人が読んでわかりやすい」という点が得られる恩恵は大きいですね。
実際のコード
最後に、実務案件の中で書いた「命令的なコード」と「宣言的なコード」を比べてみます。
(コードはオープンソースとして公開しているものになります。)
以下のコードは同じ処理を表しており、「SlackのワークスペースID、チャンネルID、ユーザーIDを登録する」
処理になります。
- 「命令的なコード」
public function store(Request $request)
{
$team_id = $request->input('team_id');
$channels = $request->input('channels');
$users = $request->input('users');
$MAE = new MultidimensionalArrayEditor;
$channel_ids = $MAE->createNewArray($channels,'channel_id','slack_channel_id');
$user_ids = $MAE->createNewArray($users,'user_id','slack_user_id');
$slack_team_model = new SlackTeam;
$slack_channel_model = new SlackChannel;
$slack_user_model = new SlackUser;
$team_id_record_existence = $slack_team_model->getTeamIdExistence($team_id);
$channel_id_record_existence = $slack_channel_model->getChannelIdExistence($channel_ids);
$user_id_record_existence = $slack_user_model->getUserIdExistence($user_ids);
if($team_id_record_existence&&$channel_id_record_existence&&$user_id_record_existence)
{
$exception = new Exception("Record_Existence",400);
throw $exception;
}
DB::beginTransaction();
try{
$result_of_save_record = $slack_team_model->create([
'slack_team_id' => $team_id
]);
foreach($channel_ids as $channel_id){
$result_of_save_record->associateSlackChannels()->create($channel_id);
}
foreach($user_ids as $user_id){
$result_of_save_record->associateSlackUsers()->create($user_id);
}
}catch(Exception $e){
DB::rollBack();
throw $e;
}
DB::commit();
return response()->json_content('201','Resource_Created');
}
- 「宣言的なコード」
public function store(SlackRequest $request)
{
$validated = $request->validated();
SlackTeam::registerSlackResources($validated['team_id'], $validated['channel_id'], $validated['user_id']);
return response()->json_content(201, 'Resource_Created', 201);
}
パッと見で違いますよね。。。というか、上の方は僕も読みたくないです笑
もちろん上の例が絶対解というわけではありませんが、ただ単に上から流れる処理を書いているのと、何をしているか端的に表しているのと、結果は同じでも読み手のメンタル的には別もののように感じるのではないかと思います。
また、「宣言的に」書こうとしてみて「これ大事だな」と思ったことがあります。
それは「命名」が超大事なんだな
ということです。
(実際に現場で働かれてる方からすれば当たり前なことかと思いますが)
というのも、「宣言的に」書くということは端的に「これ!」そして「これ!」という様に表現していく必要があります。そのため、変数1つにしろ、メソッド1つにしろその定義範囲や形容が適切でないと、「何してるのこれ...?」になりかねないことを実感しました。
現職に転職してきた元プログラマの先輩が、「命名はめっちゃ悩んだ。コード書く時間より長かった」と言っていたその片鱗を垣間見た気がします。
まとめ
- 「宣言的」なコードは「人」にフォーカスした書き方(読みやすいか)
- 「命令的」なコードは「コンピュータ」が理解する流れの書き方
- 「宣言的」かどうかのチェック観点の1つは「人が読んでサッと理解できるか」
- 「宣言的」に書くと可読性が上がって「開発」「保守」面で嬉しい
- 「宣言的」に書く上で重要な意識付けは「何をしたいか」を「適切な名詞(動詞)」で表現しようとすること
ここまで読んでいただきありがとうございました!