はじめに
自分の職場では、プログラムの設計や開発環境構築、コーディングやテストなどで、非効率なやり方だと感じる部分がちょいちょいあり、そろそろ本格的に改善したいという思いが強くなっています。そこで、改善案の参考にしようとこの書籍を読みました。
モダンC言語プログラミング 統合開発環境、デザインパターン、エクストリーム・プログラミング、テスト駆動開発、リファクタリング、継続的インテグレーションの活用
- 作者: 花井志生
- 出版社/メーカー: KADOKAWA
- 発売日: 2019/01/31
- メディア: 単行本
- この商品を含むブログを見る
今回の記事は、これを読みながらTwitter上でメモしたものになります。
メモ
テスト駆動開発で重要となるツール
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月23日
・単体テストツール
・カバレッジ測定ツール
・モック化ツール
カバレッジ測定ツール
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月23日
コードの中から実行された箇所を明らかにして、テストのカバー率を算出する。
テストが不十分な箇所を特定出来る。
相互作用を行う相手側のコンポーネントが外部に依存しているとテストがし難くなる。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月23日
相互作用相手にはモックと呼ばれるニセモノを用意して、相互作用でやり取りされるデータが正しい事のみ検証する。
継続的インテグレーション(CI)の考え方
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月25日
コードを1文字でも変更したら、即座にコードをコンパイルしてテストを実行し、アプリケーションが壊れていない事を検証する。
プログラムの中でターゲット機の制御に関係する部分を適切に切り離す事で、単体テストの中で発見したバグは、PC上でビジュアルデバッガを用いてデバッグ出来るようになる。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月25日
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月25日
コードの変更をコミットしたら、サーバーはそれをトリガーにビルドを実行する。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
↓
単体テストを実行して、アプリが壊れていない事を確認する。
↓
可能なら、ターゲット機への書き込みをして、一連のテストを実行。
スモークテスト
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
プログラムのターゲット機への配備が正常に行われた事を確認するためのテスト
CIサーバーでビルドする意義
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
ローカルPCでビルドすると、知らないうちに関係のないソースやライブラリが混入して、それを暗黙の前提としたビルドになってしまう。
コミットされて他の人のPCに配布された時、他の人の環境ではビルドが通らなくなってしまう。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
CIサーバーが正式なビルドであり、ビルドの正統性は全てここで統一される。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
ソースコードの変更と、それのビルド状況と履歴をレポートする機能を持つ。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
入力値のチェック機能を汎用化する。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
入力値をチェックする汎用的な役割を持つ構造体を作る。
最初のメンバは関数ポインタ。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
引数に構造体へのポインタと、検証する値を受け取って、検証結果をboolで返す。
2つめのメンバは、検証に使用するデータを保持する。検証の種類によって千差万別なのでvoidポインタにして、ポインタならなんでも受け取れるようにする。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
3つめのメンバは、検証パターン構造体とする。
— Shisato Yano (矢野 詩知) (@4310sy) 2019年1月26日
値の範囲チェックなら最小値と最大値、以前の値との比較なら前回値がメンバになる。
4つめのメンバは、関数ポインタに指定する検証用関数。voidポインタをキャストしてから、必要なデータを取り出して検証処理を行う。
— Shisato Yano (矢野 詩知) (@4310sy) January 26, 2019
オブジェクト指向の要件の中で特に重要なのが、多態性である。
— Shisato Yano (矢野 詩知) (@4310sy) January 26, 2019
外から見たら見分けがつかないのに、中の振る舞いは異なる、という特性。
データと処理をセットにして分離し、構造体と関数ポインタを用いて多態性を実現する。
— Shisato Yano (矢野 詩知) (@4310sy) January 26, 2019
構造体の先頭アドレスと、最初のメンバのアドレスが一致する事を利用すれば、C言語でも継承、拡張を実現できる。 pic.twitter.com/vwkVVsmfDC
— Shisato Yano (矢野 詩知) (@4310sy) January 26, 2019
同じ振る舞いを持つオブジェクトが複数あり、中にある程度の個数の関数ポインタを持ち、そのオブジェクトを複数生成する場合は、仮想関数テーブルを導入する。
— Shisato Yano (矢野 詩知) (@4310sy) January 26, 2019
ステートパターン
— Shisato Yano (矢野 詩知) (@4310sy) January 27, 2019
Stateという「共通のインターフェース」を用意しておき、多態を活用して状態によって実際の処理内容を入れ替える。
条件分岐が増えていくと、動作を決定するロジックがあちこちに分散してしまい、全体の動作が分かりにくくなる。
— Shisato Yano (矢野 詩知) (@4310sy) January 27, 2019
複数の状態セットが動作に関係する場合、それらの組み合わせを新たな状態として考えることで、一つの状態遷移として扱えるようになる。
テスト駆動開発
— Shisato Yano (矢野 詩知) (@4310sy) January 27, 2019
あるロジックを作成、改良するにあたって、まずそのロジックが正常に動作している事を確認するための自動テストを先に書き、そのテストが失敗する事を確認する。
次にテストが通るようにロジックを実装して、テストが通る事を確認する。
このサイクルを非常に小さな修正単位で繰り返す事でソフトウェアを構築する。
— Shisato Yano (矢野 詩知) (@4310sy) January 27, 2019
ハードウェアにアクセスするようなコードは単体で自動テストができないので、こうした所を適切に分離しておく必要がある。
リファクタリング
— Shisato Yano (矢野 詩知) (@4310sy) January 28, 2019
「外部インターフェース」を変更せずに、その中身を改善する作業を指す。
C言語における外部インターフェースとは、ヘッダファイルに書かれる内容である。
ロジックの内容をベースに命名すると、それらは作る人の頭の中を表した、内向きの顔になってしまう。そのクラスを使う人に対して、機能を適切に表してはいない。
— Shisato Yano (矢野 詩知) (@4310sy) January 28, 2019
C言語であれば、ヘッダーファイルに定義する構造体や関数は、クラスを使う人に対して、クラスの機能を表す外向きの顔となる。
まず最初に、使う人の立場に立って、インターフェースを決める。
— Shisato Yano (矢野 詩知) (@4310sy) January 28, 2019
つまり、これから作るクラスの使い方を表す、サンプルプログラムを考える。
サンプルプログラムを書けば、クラス名や関数名、引数といったインターフェースが全て決まる。
クラスや関数の名前は、サンプルプログラムのコードが、自然言語として読めるかどうか、で判断する。日本語に訳した時に、そのままきちんとした文章になっていれば、適切な名前と思ってよい。
— Shisato Yano (矢野 詩知) (@4310sy) January 28, 2019
呼び出し側から見た時に、自然で、十分で、冗長がないかどうか、がポイントになる。
リスクの少ないリファクタリング方法
— Shisato Yano (矢野 詩知) (@4310sy) February 1, 2019
ツールで行える変更
範囲が局所的である変更
間違えてもコンパイル、リンクエラーで発見出来る変更
不必要に厳密なテストを書かない。ケアレスミスを発見するためのダブルチェックくらいに考えるのがよい。
— Shisato Yano (矢野 詩知) (@4310sy) February 1, 2019
カバレッジ100%にこだわらない。テスト可能にするのが困難な箇所もある。
— Shisato Yano (矢野 詩知) (@4310sy) February 1, 2019
継続的インテグレーションの考え方
— Shisato Yano (矢野 詩知) (@4310sy) February 2, 2019
ビルドサーバーを用意して、リポジトリを監視し、コードが変更されたらその時点のリポジトリ上のファイルでビルドを行い、自動テストを実行する。
— Shisato Yano (矢野 詩知) (@4310sy) February 2, 2019
これにて読了。
— Shisato Yano (矢野 詩知) (@4310sy) February 2, 2019
今の職場では個人個人で手順書に沿って開発環境を構築して、それで正常にビルド出来るかを、他の人にもビルドしてもらって比較する、という工程がある。
そういった面倒な工程を無くす為にも、全員が共通環境でビルドするサーバーは必要だな。