SSブログ

眠りたいSEの眠れないブログ

↑ フィード・ティッカーを使ってみましょう


サイクリングに関する内容はHiloker.netに移動しました。(古いものは、Hiroのサイクリング&写真記録庫
よろしければどうぞ。

ソフトウェア工学的な話 ブログトップ

具体的な目的を設定しよう 「デバッギング ザ デベロップメント プロセス」の紹介 [ソフトウェア工学的な話]

今回はちょっと真面目なお話をしようと思います。

a0055_000832自分が、社会人になってから書籍には相当な影響を受けてきています。

その中で特に大きな影響をうけた本の中の1冊がこの「デバッギング ザ デベロップメント プロセス」です。

Amazonの書評では、マネージャに読んで欲しいなど、ある程度管理職向けの書籍と紹介されています。

しかし、私は若手の方にも読んで欲しいと思っています。というのも自分自身この本を読んだのは1年生か2年生位の時だったからです。

続きを読む


ソフトウェア工学について その4(2) 条件判断は単純にする [ソフトウェア工学的な話]

条件が複雑になるとテストが難しくなる。
ここでいう条件が複雑とは判断分の中でいくつもの条件のAND、あるいはOR等をとっているものである。
条件が増えれば増えるほどにパターンは増大する。
ANDやOR条件が複雑なものはうまく動作しない時に何が原因なのかを見定めることが難しくなる。
たとえば

 

if( person != null && person.getID().isnull()==false && person.getName().isnull()==true && person.age >= 20 && person.age<=60){

    //入力内容が正しい場合の処理
:

}else{

    //入力エラーの場合の処理
:
}

上記のような条件文をテストし、うまく動作しなかったらどこが原因かをいかにして特定すべきだろうか。
この場合条件を構成している5つの条件を個々に見ていく必要が出てくる。テストケースもそれなりのケース数になるだろう。
たとえば下記のようにしたらどうだろうか。

private boolean isValidName( USER person ){
    if( person != null && person.getName().isnull != true){
        return true;
    }else{
        return false;
    }
}

private boolean isValidID( USER person ){
    if( person != null && person.getID().isnull != true){
         return true;
    }else{
         return false;
    }
}

private boolean isValidRangeOfAge( USER person ){
    if( person.age >= 20 && person.age <=60 ){
         return true;
    }else{
         return false;
    }
}

(中略)

// 入力データが有効か判定
if (isValidName(person) == true && isValidID(person) == true && isValidRangeOfAge(person) == true) {

     //入力内容が正しい場合の処理
                        :

}else{

     //入力エラーの場合の処理
                         :
}

 


見たところ、だいぶプログラム自体は長くなっている。
しかし、誤りがあった場合についても調査は格段に楽になるのではないだろうか。
たとえば、各関数に対して判定結果をログ出力するような修正も比較的簡単にできる。
また、それぞれの関数をあらかじめテストすることによって、問題の切り分けが容易になるはずである。
そういった理由から、複雑な条件判断について単純にすることは有効であろう。
また、ソースについても可読性が向上するはずである。意図を組みやすくなったのではないだろうか。

今回は簡単な例だったので関数をいくつも作ることの面倒さのほうが目立つかもしれないが、もっと複雑判定がいくつも出てくるようであれば、このようにすることも一考の価値があるだろう。


ソフトウェア工学について その4(1) ルーチンはコンパクトにする [ソフトウェア工学的な話]

すべての機能が同じルーチンに書かれているなど、巨大なルーチンを作成してしまうとテストは難しくなるということについて考えてみたい。
プログラムには条件分岐がある。この条件分岐が出た場合普通にテストケースは増大する。それはホワイトボックステストの場合は、条件を網羅するためのテストケースが増大すること。ブラックボックステストとしても、おおむね条件分岐が多ければ多いほど使用は複雑になるためにテストケースは多くなってしまうからである。

プログラムが長くなればなるほど一定の割合で条件分岐が出てくる。つまり長いルーチンはその長さに比例して1つのプログラムにたくさんの条件分岐が出てくるということである。

そのため、条件を網羅するためのテストケースが指数関数的に増大するからである。
たとえば、5行につき1つIF文が出てくるとすると、10行では2つ、20行では、4つ、100行では20にもなる。
さて、これらのIF文をすべて条件網羅によるテストを行わないとした場合どうなるだろうか。
10行の場合は2の2乗で4ケース
20行の場合は、2の4乗で16ケース
100行の場合は2の20乗・・・つまり100万ケース程度となる。
実際にはIF以外にもテストケースを増やす命令は、Switch~Case、論理式や3項演算子、ループなど多数存在する。
こういったことからも、1つの大きなルーチンを作ることが飛躍的にテストを面倒にすることはわかるのではないだろうか。
このようにルーチンが巨大になることによるテストケースの増加についての有効な対応策としては、月並みかもしれないがある程度の大きさで分割し、サブルーチン化してしまうことが重要である。

たとえばさっきの100行のプログラムを20行のサブルーチン5つにすればテストケースはどうなるだろうか。
16x5=80、こうしてしまえばおのずとコントロールは容易になる。

20行のサブルーチンに分割することができてしまえば、それぞれのサブルーチンをテストし、1つずつ確認すればいいので、テストは容易になる。

サブルーチンを適切な大きさにしておくのは、共通化を目的とすることだけでなく、ある一定のまとまり単位でルーチンの切り出しを行うことによってテストケースを効果的に減らす事ができるということがいえると思う。

最後に念のため、分割にあたっては、20行とかいった物理的な行数で分割するのではなく、ある程度の機能単位で分割しそれぞれにわかりやすいルーチン名を付けることは言うまでもない。


ソフトウェア工学について その4 テストのしやすさを考える [ソフトウェア工学的な話]

だんだん、ソフトウェア工学の話の中でもマイノリティなところへと突き進んでいるような気がし始めているが今日はテストしやすいプログラムを作成するということについて考えたい。

プロとしてプログラムを作るのであれば当然、きちんと動くかについて保証する必要があり、そのためにテストは必要不可欠であるからである。
さて、テストしやすくするためにはどのようにすればいいのだろうか。
ひとつ簡単な方法としては、今まで振り返ってテストしにくいものはどういったものなのかを考えてみるとわかりやすいのではないだろうか。
ここでは、テストしにくいものとして以下をあげておく

①巨大なルーチン(サブルーチンを含む)
②条件が複雑なもの
③うまく動いているのかが分かりにくいもの
④特殊なタイミングを必要とするもの
⑤メモリリーク などなど

これらはすべてテストをやりにくくする。なぜテストがやりにくくなるのか。原因としてはだいたい以下のうなものになると思う。

①テストケースが膨大になり、コントロールできなくなる
 テストケースをいたずらに増やすようなプログラムはテストにかかる作業を増大させる。
 テストの設計、テスト実施、結果の検証等の時間がかかってしまう。
 これについてはプログラムの構造、条件判断などの設定に注意すればかなり簡単に回避することができるだろう。

②再現性を持たせることができないのでテストの終了の判断がしにくい
 問題が起きた時に再現できないのはテスト作業を増大させる。
 問題はどういった条件で起こるのか。問題はどこで起きているのかなど。
 完全に避けることはできないものの、ログの出力などを行い、問題発生個所を特定できるような構造とする癖をつけ
 ることである程度は回避することができる。

③おびただしい手順や時間を必要とする
 問題が発生するためにはたくさんの操作を行う事で発生する。
 あるいは数時間動かすと異常終了するなど。
 メモリリークなどはこれにあたる。もっとも根気を必要とする。
 lint等のツールでチェックする、あるいはコードレビューを徹底するなどして入り込まないような工夫が必要である。

次回以降、これらについてみてきたいと思う。


ソフトウェア工学について その3(4) 空行の使い方について [ソフトウェア工学的な話]

今日はこじんまりとした話題になるが空行について話をしたい。
空行とは文字の通り空白の行である。
プログラムのドキュメント化を行う当たっては空行も大切な要素である。
空行はコーディングをするにあたっての1つのブロックの切れ目を表現することを可能にする。
文章でいえば、段落の切れ目を表現することが可能だ。

先日話をしたコメントとこれを上手に組み合わせるととてもわかりやすいソースを書くことができるようになる。

たとえば、下記のプログラムを見てほしい。

リスト1 空行がない例

  // 文字の入力
  int result = scanf("%s",&b);
  // 入力された文字列が"string"であるかを比較する
  while( a[pos] != 0 && b[pos] != 0 ){
    if( a[pos] != b[pos] ){
      // 内容が異なっている場合は結果をfalseにする
      r = false;
      break;
    } // if
  } 

この場合はそもそも母体が小さいので容易かもしれないが、とくに行コメント(1行丸ごとコメントであるものを指す)を行った際はその段落単位で空行を入れると非常に見やすくなる。

リスト2 適切な空行が入った例

  // 文字の入力
  int result = scanf("%s",&b);

  // 入力された文字列が"string"であるかを比較する
  while( a[pos] != 0 && b[pos] != 0 ){
    if( a[pos] != b[pos] ){

      // 内容が異なっている場合は結果をfalseにする
      r = false;
      break;

    } // if

  }

 
上記リスト2のようにすると「//文字の入力」とある個所はどこまでのロジックを指すのか。
入力された文字の比較をしているのはどこまでなのか等を見分けるのが容易になるかと思う。

このようにちょっとした工夫を積み上げることによってソースは一段と可読性を上げることができ、ドキュメント性が増す。


ソフトウェア工学について その3(3) Whyの視点でコメントを記述する [ソフトウェア工学的な話]

コーディングをするとき、コメントを書くことは必要に応じて行われるが、コメントにはセンスが必要だと思う。
たとえば、コードを読めばわかるようなコメントというのは私の意見としてはうっとおしいと感じる。
よくいわれることであるが、コメントの鉄則は「Why」の視点に立って書くことが必要である。
Whyの視点に立つと、コメントはどう変わるのだろうか。

たとえば以下のプログラムを見てほしい。
何をしているのかがすぐにわかるだろうか。

リスト1

void main()
{
  // 変数Aに"string"を代入
  char  a[]  = "string";
  //入力文字格納用
  char  b[100];
  //posを0で初期化
  int       pos=0;
  //rをtrueで初期化
  bool  r  = true;

  // 入力した文字を変数bに代入
  int result = scanf("%s",&b);
 
  // aのpos番目が0以外  かつ  bのpos番目が0以外の間繰り返し
  while( a[pos] != 0 && b[pos] != 0 ){
    // もしもaのpos番目とbのpos番目が違うならば
    if( a[pos] != b[pos] ){
      // rにfalseを代入
      r = false;
      // ループを抜ける
      break;
    }
    // posを1増やす
    pos++;

  }
  // rの内容を表示
  printf("%d\n",r);


以下のようにした場合どうであろうか。
リスト2
void main()
{
  // 比較文字列[string]
  char  a[]  = "string";
  // 入力文字の格納用
  char  b[100];
  // 入力文字と比較文字列を比較するための変数
  int       pos=0;
  // 比較結果 true:入力文字は"string"と一致
  bool  r  = true;

  // 文字の入力
  int result = scanf("%s",&b);
 
  // 入力された文字列が"string"であるかを比較する
  while( a[pos] != 0 && b[pos] != 0 ){

    if( a[pos] != b[pos] ){

      // 内容が異なっている場合は結果をfalseにする
      r = false;
      break;
    } // if

    pos++;

  } // while

  // 比較した結果を表示
  printf("%d\n",r);

} // main

 

随分と見やすくなったのではないだろうか。
リスト1はコメントを「How to」の視点で書いている
つまりどのようにするかをそのまま書いているだけて、ソースをみればすぐにわかってしまう。
リスト2はコメントを「why」の視点で書いている。
リスト2はコメントがソースで行おうとしている処理の意図を記述しているので、読むのが比較的容易であろう。
一方、リスト1は何をしているのかはわかるがその意図はくみ取りづらいので何をしているかを読み取るのに労力がかかってしまう。
Whyの視点でコメントを書くには「この処理はつまり何をしているのか」、あるいは「この処理が亡くなったらどうして困るのか?」ということを考えて書くとわかりやすく書くことができると思う。
このようなことを心がけるだけでソースの可読性はよくなる。

ソフトウェア工学について その3(2) 変数名について [ソフトウェア工学的な話]

 前回の話とにているが、今回は変数について触れてみたい。
よく、経験の浅いプログラマは一時的な変数を作るときにA,Bやi,jなどの変数を使う事がある。
昔はメモリが小さいことなどから言語のコンパイラやインタプリタの仕様で変数が2~3文字しか使えない処理系等があったので致し方ないこともあったが、現代においてはこのような制約はないため、これらの1~数文字の意味のわからない変数名は使うべきではないと思う。

たとえ、1回しか利用しないようなその場限りの変数であっても変数名を適切に設定することでドキュメント性を向上することができるからである。
以下に例を示す。

1:int numberOfUser = MySystem.getNumberOfUser();

2:for (int userIndex = 0;userIndex < numberOfUser;userIndex++){
3:  Person currentPerson = MySystem.getUserInformation(userIndex);
4: String phoneNumber = currentPerson.getPhoneNumber();
                        :
n:} 


上記のような変数名を設定したら、currentPersonやphoneNumberはどういった情報が入っているだろうか。
容易に想像がつくはずである。
このように普段から少しの手間をかけ、変数名を適切にすることでソースの可読性は非常に高くなるはずである。

また、マジックナンバーを使わないことを心がけることも重要である。
マジックナンバーとは、プログラムのどこかに説明なしで使われている1,2,10,15等の数値リテラルのことである。
たとえば、以下のコードを見てほしい。

 

1:if( currentPerson().isAdult() == 0 ){
2:  System.out.println( message[10] );
3:}

ここで示されている0や10はどういった意味があるのだろうか。たとえば下記のように書いてみたらどうだろう。

1:if( currentPerson().isAdult() == false ){
2:  System.out.println( message[ err_not_a_voting_member ] );
3:} 

上記のように書かれていれば、大人でなければ投票できない旨のメッセージを表示する処理をしているということが容易に理解できるのではないだろうか。
他にも定数を使うと変更を確実に行うことができる等のメリットもでてくるので、マジックナンバーを使わずに適切な名称の変数を使うことが望ましいだろう。


ソフトウェア工学について その3 プログラムのドキュメント化を心がける [ソフトウェア工学的な話]

理想的なプログラムソースとは、究極的にはドキュメントとしても利用可能なものであると思う。
たとえば、以下のソースは何をしようとしているのであろうか。

1:if(a.x() != 0){
2:  b.cd();
3:} 

これでは、誰にもわからないであろう。
このような場合、コメントをつけることでわかりやすくすることが普通に行われるだろう。

1:// 利用者がいる場合は、全体に通知する。
2:if( a.x() != 0){
3:  // 利用者がいる場合のシステム終了通知処理
4:  b.cd();
5:}
 

上記のようにすることでだいぶ見やすくなったのではないだろうか。
さらに以下のようにしてみるとどうなるであろうか。

1:if( MySystem.NumberOfUser() != 0 ){
2:  AllOfUser.NotifySystemTermination();
3:}

上記ソースをみれば、コメントがなくてもシステムの利用者が0人でない場合は、すべてのユーザにシステムの終了を通知することが分かるのではないだろうか。
上記の場合、変数名やメソッド名を適切にすることによって処理の内容が自明になっていることが分かる。
日ごろからこのような心がけを行うことによってコメントは少なくとも非常に分かりやすいソースを記述することができると思う。


ソフトウェア工学について その2 エラーメッセージに対する心がけ [ソフトウェア工学的な話]

ソフトウェア工学についての話題はいろいろあり、本来であれば体系だてて話をすべきなのであろうが、ネタの整理などもできていないので、思ったことを少しずつ記事として投稿していくこととしたい。
私のほうでいずれ整理しようと思う。

今日は、ソフトウェア開発に関するスタンスについての話である。物事に対するアプローチ、考え方として必要と考えるので、これからも述べていきたいと思う。

ある日、会社で経験の少ない後輩がプログラムを作成し、うまくいかないので教えてほしいということがあった。
話を聞いてみるとコンパイルがうまくできないとのことであった。
さっそく目の前でコンパイルをしてみるとおびただしい数のエラーや警告メッセージが出てきていた。
私が、最初のメッセージについて「これはなぜ出ているのか?」と聞いたところ、「よく出るんです。でも、これは特に問題ないですから。問題はこれなんです。」と、続けてくる。
もしこう考えることがあるのであればその前に以下の2点を考えてほしい。
 ・いったいこの警告は何を意味しているのか。
 ・なぜコンパイラは私に警告をする必要があるのか。
概ねコンパイラなどのツールが警告メッセージを出すのは何か問題があるからである。それは曖昧さや論理的な問題などである。
たとえば、変数を宣言しているが使っていないとか、宣言していない変数を使っているなどである。
たとえば以下のようなプログラムを考えてみる。

 

C言語による誤ったプログラムの例 

1:void main(){
2:  int x;
3:  x=x+2;
4:  printf("%d\n",y);
5:}
 

これをコンパイルすると以下の警告が表示されるはずである。
3行目で初期化していない変数Xを利用していること
4行目で宣言していない変数Yを利用していること

このソースを書いた人(=開発者)はここで以下のことを考える必要があるのではないか

  1. 変数Xは最初何であることを期待していたのであろうか?0,1、それともどんな数字でもよかったのか?
  2. 4行目ではもしかしたらxの計算の結果を表示したかったのではないか?

これらの問題はコンパイル時の警告メッセージをきちんと読めばわかることである。
エラーメッセージがでず、問題の原因も不明なまま何日も何日も調査を続けることなどはよくある話である。
メッセージが出てきた場合にはきちんと内容を理解し、対処するように心がけたいものである。


ソフトウェア工学(エンジニアリング)について その1 [ソフトウェア工学的な話]

 私は初めてコンピュータを触ったのはちょうど8歳の時、友人がPC-6001mk2というパソコン(当時はマイコンと言っていた)を買ったのを触らせてもらったのが初めてだった。
 中学に入り、MSXというパソコンを購入して、BASICという言語でプログラムを作り、アセンブラをかじり、それこそ1日10時間以上パソコンをいじっていても苦にならないくらい遊んでたのを覚えている。
 高校に入り、ゲームやユーティリティ作りばかりをやっていて、友人と誰もが難しいと思っていたことを実現することに挑戦することが楽しかった。たとえばMSXを使っていたときはBasic言語の拡張に挑戦したり、漢字表示のできないPCで漢字を表示するための機能を開発したり、画面の回転処理をアセンブラで作りこんで、少しでも早くなるようなアルゴリズムの改良に気持ちを弾ませていたなど。その時に読んでいたのはハッカー向けのパソコンの内部資料(BIOS、I/Oアクセスの方法、果てはフロッピーディスクコントローラの制御方法などに至るまで)が中心だった。
 大学を卒業するときに自分が衝撃を受けたのは、スティーブ・マコネル氏の「Code Complete」を読んだ時だ。この本は1冊7,000円したので、ちょっと手が出しづらかったのだが、就職も決まったので、プロとして知っておく必要があると思って買ったのである。
 この本にはソフトウェアの開発を行うに当たって、必要なこと(特に開発について)がシステム開発のフェーズ(工程)単位にまとめられており、チームで開発を行うために必要なノウハウやデバッグの仕方、設計について、テストの考え方などさまざまなことが書かれていた。変数の名前1つ、コメント1つの意味・意義についてがまとめられていて、当時値段を理由に購入を戸惑っていたことを後悔したほどの内容だった。
 さて、前置きが長くなってしまったが、ソフトウェア工学とはどういうものかについて考えてみる。スティーブ・マコネル氏は「ソフトウェア開発プロフェッショナル」の中で「ソフトウェアでの科学と工学の違いは、他の分野での違いと同じである。科学者は真理を探求し、仮説を検証し、対象分野を深く掘り下げようとする。一方、技術者は現実の問題を解決するために、真理を探究し、有用なものを見つけ出し、周知の事実を適用するのだ。科学者が、最先端の研究を進め、いつも最前線に立っている必要がある。一方技術者は、信頼性が高く、効率のよい方式を熟知していなくてはならない。」とのことである。つまり実用に即したものであるとくことである。
 車を例に考えてみると、最先端の研究をすすめ、最前線の技術を投入した車といえば、その1つにレーシングカーがあげられる。レーシングカーは早く走るという点を追求した車で素材や技術など最新のものを投入して造られているだろう。また、「いかに早く」を追求した結果、居住性、燃費、コストパフォーマンスなどはある意味度外視されているだろうし、車にはプロのメカニック担当がついていて常に整備を怠らないようにしている。
 一方、普通に市場で入手可能な車を考えると、居住性、燃費、価格についてなどは重要な要素になるし、日常のメンテナンスに至っては私たちのような素人でも扱うことができるなど全く性質の異なるものである。
 前者のような車づくりが「コンピュータサイエンス(科学)」の範疇であるのにたいして、後者は「ソフトウェアエンジニアリング(工学)」の範疇であるということになる。
 次回以降、機会があればさらに考えていきたいと思う。
Code Complete第2版〈上〉―完全なプログラミングを目指して

Code Complete第2版〈上〉―完全なプログラミングを目指して

  • 作者: スティーブ マコネル
  • 出版社/メーカー: 日経BPソフトプレス
  • 発売日: 2005/03
  • メディア: 単行本
Code Complete第2版〈下〉―完全なプログラミングを目指して

Code Complete第2版〈下〉―完全なプログラミングを目指して

  • 作者: スティーブ マコネル
  • 出版社/メーカー: 日経BPソフトプレス
  • 発売日: 2005/03
  • メディア: 単行本
ソフトウエア開発プロフェッショナル

ソフトウエア開発プロフェッショナル

  • 作者: スティーブ・マコネル
  • 出版社/メーカー: 日経BP社
  • 発売日: 2005/01/20
  • メディア: 単行本

ソフトウェア工学的な話 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。