ソフトウェア工学1
第
8回 コーディングとレビュー
2017年6月6日
中島 毅
授業内容
• コードを美しく書くことの意味
• 構造化プログラミングとレビュー
• 設計レビューとコードレビュー
• 静的解析ツール
世の中の開発は流用ベースが多い
初期製品開発(顧客
A向け)
シリーズ開発(顧客ニーズ追随)
機種分岐
XA-0
XA-1
XA-2
XA-3
XB-1
XB-2
市場ニーズの変化 テクノロジーの進歩 保守コストの増大YA-0
次機種開発
YA-1
XB-3
YB-1
顧客(分野)A 顧客(分野) B製品ライフサイクル
要求 分析 設計 製造 テス ト開発ライフサイクル
企画基幹業務(人事,資材,経理,・・)のシステム
FA, エレベータ,エアコン,エンジン制御,カーナビ・・・
分野別開発
(仕向地,
グレード)
読めないプログラムでは開発が回らない
新
規
改
造
完全流用
生産量の
90%以上が完全流用という開発プロジェクトがほとんど!
新規:
全く新しく作る部分
改造:
既存のプログラムを一部修正
して作る部分
完全流用: 製品に組み込まれているが,
一切修正せず利用する部分
5kL
100kL
1人しかアサイン
されない
開発量
生産量
知っている人が
少ない/いない
読めないプログラムでは開発が回らない
既存のコードが読めないと、改造や不具合の原因追求ができない
新
規
改
造
完全流用
欠陥は
主にここ
に出現
使い方のミス
(インタフェース)
にも欠陥あり
既にあった
欠陥の顕在
化もあり
流用する部分を知らないで,新規・改造
部分を作るのは容易でない
読めるプログラムを作ってほしい!(切なる願い)
改造
不具合の
原因追求
× どう改造してよいか?
× バグの原因はどこ?
•
設計書がない!
•
ソースコードが読めない!
品質悪化 & コスト増
授業内容
• コードを美しく書くことの意味
• 構造化プログラミングとレビュー
• コーディング規約
• 設計レビューとコードレビュー
• 静的解析ツール
よいプログラムを書くには
• 良いプログラムとは
– 欠陥がない
– 移植性がよい
– 性能がよい
– テストがしやすい
– シンプルである
(無駄がない)
– 自己説明的である
– 自己証明的である (見て正しいことがわかる)
基本作法
構造化プログラミング
構造化プログラミング
Edsger W. Dijkstra 登場!
・オランダの数学者 (チューリング賞受賞者)
・構造化プログラミング提唱者
プログラミングに,数学の証明の
考え方を導入すれば,プログラム
に欠陥が入らなくなる.
プログラムは、作るほど欠陥が入る。何とかならないか?
構造化プログラミング
(仕様の具体化)
•プログラミング=
仕様を段階的に具体化
していく過程
Structured programming とは
具体化
親処理
子処理
1
子処理
2
子処理
3
親処理
子処理
1
子処理
2
子処理
3
条件
具体化
親処理
子処理
1
条件
順次
分岐
反復
(ループ)初期化
•N階層はN+1階層へ
順次/分岐/反復
の3種類の制御構造
のいずれかを使って
仕様を分解
例:
生年月日と今日の日付から、年齢を計算するプログラム
今日が誕生日前
後か判定する
年齢を決定する
判定
=後
日の前後で判定する
現在月
>生月 =生月 <生月判定
=前
現在日
>=生日 <生日判定
=後
判定
=前
判定
==後
年齢=現在年
-生年
年齢=現在年
-生年-1
プログラミング時
// 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result; // 今日が誕生日前後か判定する // 年齢を決定する return result; } // 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result; // 今日が誕生日前後か判定する // 年齢を決定する return result; }
コーディング
(
Cプログラム)
• シンプルである
• 自己説明的である
• 自己証明的である
// 年齢計算int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result;
// 今日が誕生日前後か判定する
if (cmonth > bmonth) { afterBirthday = yes; }
else if (cmonth == bmonth) {
// 日の前後で判定する } else { afterBirthday = no; } // 年齢を決定する return result; } // 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result;
// 今日が誕生日前後か判定する
if (cmonth > bmonth) { afterBirthday = yes; }
else if (cmonth == bmonth) {
// 日の前後で判定する if (cday >= bday) afterBirthday = yes; else afterBirthday = no; } else { afterBirthday = no; } // 年齢を決定する return result; } // 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result;
// 今日が誕生日前後か判定する
if (cmonth > bmonth) { afterBirthday = yes; }
else if (cmonth == bmonth) {
// 日の前後で判定する if (cday >= bday) afterBirthday = yes; else afterBirthday = no; } else { afterBirthday = no; } // 年齢を決定する if (afterBirthday == yes) result = cyear - byear; else
result = cyear - byear - 1; return result;
}
プログラミング時
// 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
構造化プログラミングとレビュー
(証明過程)
プログラムの正当性は
N階層とN+1階層を比較して確認
補題1・補題2・補題3
⇒命題A
数学的
帰納法
数え上げ
•
n=1のとき成立
•
nのとき成立するとして
n+1を証明
親処理
子処理
1
子処理
2
子処理
3
親処理
子処理
1
子処理
2
子処理
3
条件
親処理
子処理
1
条件
順次
分岐
反復
(ループ)初期化
補題1
|補題2|補題3
⇒命題A
命題
A
命題
A
補題1
補題2
補題3
命題
A
補題1
補題2
補題3
比較 比較 比較// 年齢計算
int age(int byear, int bmonth, int bday, /* 生年月日 */
int cyear, int cmonth, int cday) /* 現在年月日 */
{
enum question {yes, no} afterBirthday; int result;
// 今日が誕生日前後か判定する
if (cmonth > bmonth) { afterBirthday = yes; }
else if (cmonth == bmonth) {
// 日の前後で判定する if (cday >= bday) afterBirthday = yes; else afterBirthday = no; } else { afterBirthday = no; } // 年齢を決定する if (afterBirthday == yes) result = cyear - byear; else
result = cyear - byear - 1; return result; }
コーディング
(
Cプログラム)
• シンプルである
• 自己説明的である
• 自己証明的である
⇒コードレビュー
がやりやすい!
レビュー時
int age(int AY, int AM, int AD, int BY, int BM, int BD) {
int X, Y;
X=0;Y=BY-AY;
if (BM>AM)X=1;
if (BM==AM&&BD>=AD)X=1;
if (X==0) Y-=1;
return Y;
}
参考: 等価なプログラム
•
変数名が記号になっている
⇒
変数とそれを使った式の意味は?
•
インデントや改行がない
⇒
構造はどうなっている?
•
if文がelse項を持たない
⇒
場合分けがどこを通る?
•
計算の順番がまとまっていない
⇒
どの計算結果がどこで使われる?
読んで理解できない,正しさを確かめられない
何が悪いか
コーディング規約
プログラム自体を説明的にしよう!
•ヘッダコメント
•変数コメント(引数/内部変数)
•ブロックコメント
コメントを書こう!
•画面に収まる範囲の大きさにモジュールを作る
•統一的でわかりやすい関数名/変数名を使う
•統一的な制御構造を使う
•統一的なレイアウト(空行、空白、改ア行)
コーディング規約
読みやすさの追求
コメント例
/*******************************************
*** Designer
: 中島 毅
*** Date
: 2003.6.1
*** Purpose
: 三角形の種別を判定するプログラム
*******************************************/
#include <stdio.h>
enum Result {ordinalTriangle=0, isoscelesTriangle=1,
equilateralTriangle=2};
int main()
{
float x, y, z;
/* 三角形の3辺 */
enum Result myResult; /* 判定結果 */
extern enum Result checkTriangle(int, int, int);
//データ読み込み
fprintf(stderr, "x = "); (void)scanf("%f", &x);
fprintf(stderr, "y = "); (void)scanf("%f", &y);
fprintf(stderr, "x = "); (void)scanf("%f", &z);
ヘッダコメント
(作成者、日付、内容等)
変数コメント
(引数/内部変数)
授業内容
• コードを美しく書くことの意味
• 構造化プログラミングとレビュー
• コーディング規約
• 設計レビューとコードレビュー
• 静的解析ツール
ユーザ 分析者
設計者
プログラマ
テスタ
検討
確認
伝達
伝達
検討
確認
要求分析/企画
設計
コーディング
テスト
要求
仕様
設計
仕様
設計レビューとは
工程を設定する
正しさを確かめ,伝えるために
ドキュメントを作成し,
確認する
ソフトウェアの品質を保証するための
人手による検証活動
18ソース
コード
検討
確認
実際、設計レビューってどうやっている?
会議形式のレビュー
•
レビューしてもらう人: 説明する
•
レビューする人:
質問する、指摘する
書面レビュー
•
書類をまわす、赤を入れる
誰が?
•
自分(セルフレビュー)
•
仲間内(ピアレビュー)
•
利害関係者(公式レビュー)
コードに含まれる欠陥の種別をあげよ
• 今までに出会ったことのあるバグを思い浮かべよう。
• 手強かったのはどんなバグか?
コードに含まれる欠陥の種類(単体レベル)
カテゴリ
種別
シンボル
シンボル取り違え
演算
演算子の取り違え
演算順序間違い
型変換誤り
初期化抜け
精度誤り
配列
配列領域からの逸脱参照
メモリ
不正領域(
Null、不定値)への書き込み
メモリリーク(開放不正)
条件式
異なる型の比較
演算子の順序誤り
精度の異なる値の比較
比較演算子の選択誤り
ループ終了時の処理の誤り
ループ脱出条件の誤り
ループ変数の変更誤り
条件組合せ 複合条件式の組合せ誤り
複数条件ブロックの組合せ誤り
設計誤り
アルゴリズムとデータ構造の不正
条件処理抜け
例外処理抜け
コードレビュー
チェック
リスト
視点1: 機能仕様満足 視点2: アルゴリズムの出来 視点3: インタフェースミス 視点4: プログラムミス レビュー結果 報告書フィードバック
コーディング
規約
① 守るべきこと
② 確認の視点
③フィードバック(改善)
ソースコード
プログラム 仕様書プログラム仕様
を満たすこと
コーディング規約
を満たすこと
ソースコードの正しさを検査するために、人が読んで確認
する設計レビュー
レビューのポイント
22コードレビューの流れ、利点/欠点
利点
– あらゆる種類の欠陥を見つけることが可能
– 一般に、欠陥を見つけるコストは、テストに比べて安価
欠点
– 人間が実施するので、レビューアの能力とやる気に、パ
フォーマンス(欠陥検出率、検出効率)が左右される
ソースコードレビューの流れ
セルフ
レビュー
ピアレビュー
公式レビュー
作成者自身
開発チーム内
関係者(設計者、テスタ、品質保
証者など)
ポカミス除去の観点
実装の適切さの観点
品質保証の観点
コードレビューの種類
ウォークスルー法
チェックリスト法
(インスペクション)
実施
方法
プログラムを机上で実行すること
でプログラムの動作を追いながら
欠陥を見つける(机上実行)
ルールとチェックリスト(CL)を
用いて、着眼点ごとに欠陥を見
つける
実施
形態
• 主に、ピアレビューで使用
• 複数人での実施が推奨
• 通常、個別に事前レビュー実施
• 主に、公式レビューで使用
• 参加者の役割が決っている
• (例)開発者、モデレータ他
利点
• コードでありがちな欠陥を検出に
向いている(CLの効用)
• 基準(ルールとCL)があるので、
品質保証として意味をもつ。
• コードの実装が仕様を満足して
いることを確認しやすい。
• プログラム仕様やコードの実装
の意味を伝えるのに向いている
(技能とノウハウの伝達)
仕様:
このプログラムは、3つの数字を端末より読み込む。この3つの値は、そ
れぞれ三角形の3辺の長さを表すものとする。プログラムは、三角形が不
等辺三角形・二等辺三角形・正三角形のうちのどれであるかを決めるメッ
セージを出力する。三角形でないケースについてもその旨を出力する。
コードレビューの体験
チェックリスト
C-1 プログラムは仕様を満たしているか?
C-2 コーディング規約にあっていないものがあるか?
C-3 プログラム自体にエラーはあるか?
コーディング規約(抜粋)
R-1 main関数は引数がないときvoidを記述する。
R-2 代入の型が違う場合には、明示的にキャスト(強制型変換)する。
R-3 if/while/forの後は必ずブロック{}にする。
/************************************************* *** Designer : 中島 *** Date : 2003.6.1 *** Purpose : 三角形判定プログラム *************************************************/ #include <stdio.h>
enum Result {ordinalTriangle=0, isoscelesTriangle=1, equilateralTriangle=2};
/************************************************ *** Function Name : checkTriangle
*** Designer : 中島 *** Date : 2003.6.1 *** Function: 3辺の長さから不等辺三角形・二等辺三角形・ 正三角形のいずれかを判定する *** Return : ordinalTriangle -- 不等辺三角形 isoscelesTriangle -- 二等辺三角形 equilateralTriangle -- 正三角形 ************************************************/ enum Result
checkTriangle(int x, int y, int z) {
enum Result myResult; // 正三角形のチェック if (x == y && y == z) myResult = equilateralTriangle; // 二等辺三角形のチェック if (x == y || y==z || z == x) myResult = isoscelesTriangle; return myResult; } /************************************************ *** Function Name : checkTriangle
*** Designer : 中島 *** Date : 2003.6.1 *** Function: 3辺の長さから不等辺三角形・二等辺三角形・ 正三角形,三角形不成立のいずれかを判定する ************************************************/ int main() { float x, y, z; /* 三角形の3辺 */ enum Result myResult;
//データ読み込み
fprintf(stderr, "x = "); (void)scanf("%f", &x); fprintf(stderr, "y = "); (void)scanf("%f", &y); fprintf(stderr, "x = "); (void)scanf("%f", &z); // 三角形判定
myResult = checkTriangle(x, y, z); // 出力
if (myResult == ordinalTriangle)
printf("Result = 不等辺三角形¥n"); else (myResult == isoscelesTriangle)
printf("Result = 正三角形¥n"); else { printf("Result = 二等辺三角形¥n"); } return 0; }
授業内容
• コードを美しく書くことの意味
• 構造化プログラミングとレビュー
• コーディング規約
• コードレビュー
• 静的解析ツール
コードレビューの支援ツール: 静的解析
静的解析ツール
プログラムを実行することなく,危険なコード部分を見つけ、警告を出すツール
ソース
コード
静的解析
ツール
①警告
②メトリクス
レポート
解析
コンパイル
直後
修正
プログラマ
不具合を含むコードの
重大問題を早期発見!
静的解析ツールの機能: 警告出力
・ 不具合に直結するコード
・ 保守性の悪いコード
危険なコードに警告を出す
早い段階で見つけ修正
できればコスト効果大!
// ネットワークノードの接続long NwDrawer::GNAtoINL(GNetwkArc* pGNA,IltLink* pINL, double fromID,double toID) { IltObject* fromINE; IltObject* toINE; // 始点と終点の決定 if (fromID >= 0) { fromINE = SearchNetworkElement(fromID); } //終点IDが正常値である場合 if (toID >= 0) { toINE = SearchNetworkElement(toID); } // どちらかのIDが異常な場合
if ( (fromINE == NULL) || (toINE == NULL) ) { return -1; fromID >= 0 なら fromINE 初期化 toID >= 0 ならtoINE 初期化