連載
NAgileで始める実践アジャイル開発

第2回 ソフトウェア開発をシンプルにする考え方のコツ

福井コンピュータ株式会社 小島 富治雄
Micosoft MVP 2005 − Visual C#)
2005/11/05

■■「シンプルさ」にコミットする演習 ― シンプルに考えるための問題意識■■

さてここで演習だ。
   
演習があるんですか。
   

当たり前だ。実践せずに理論だけでNAgileが身に付くか。

 さて問題だ。日付を表すこんなクラスがあるとする(C#)。

public class Date
{
  int year  = 2000;
  int month =    1;
  int day   =    1;

  public Date(int year, int month, int day)
  {
    this.year  = year;
    this.month = month;
    this.day   = day;
  }
}
日付を表すクラス(C#)

 まずは演習の課題として、この日付クラスに、日付をチェックする機能を付けてほしい。この日付クラスのコンストラクタのパラメータに、「0年13月32日」のような不正な値が渡されたら例外を投げるようにしてほしいのだ。分かるか?

   
はい。
   
やってみてくれ。
   

うー……。
 ………………。
 ……………………。
 (しばらくたって)……はぁ、はぁ、師匠できました。

   
だいぶ時間がかかったな。まあいい。見せてみろ。
   

はい。ちゃんと閏(うるう)年まで考慮してみました。

public class Date
{
  int year  = 2000;
  int month =    1;
  int day   =    1;

  public Date(int year, int month, int day)
  {
    this.year  = year ;
    this.month = month;
    this.day   = day  ;

    if (year < 1)
      throw new ArgumentException();
    if (month < 1 || month > 12)
      throw new ArgumentException();
    if (month == 2)
    {
      if (year % 4 == 0 && year % 100 != 0
            || year % 400 == 0)
      {
        if (day < 1 || day > 29)
          throw new ArgumentException();
      }
      else
      {
        if (day < 1 || day > 28)
          throw new ArgumentException();
      }
    }
    else if (month == 4 || month == 6
          || month == 9 || month == 11)
    {
      if (day < 1 || day > 30)
        throw new ArgumentException();
    }
    else
   {
      if (day < 1 || day > 31)
        throw new ArgumentException();
    }
  }
}
弟子がコンストラクタに実装した日付チェック機能のソース・コード
   
なるほど。出来ているようだな。
   
さすが師匠。一目で分かりますか。
   

いや、分からん。

 ところで、これはどんなロジックなんだ!? ……うーむ。追加されたコードを見たままいってみるぞ。

これは、

「作成時に、一年未満だったら例外を投げる。一月未満か十二月を超えたら例外を投げる。二月の場合、年が4で割り切れてかつ100で割り切れない場合、もしくは400で割り切れた場合、一日未満か二十九日を超えたら例外を投げる。二月のこれら以外の場合は、一日未満か二十八日を超えたら例外を投げる。四月、六月、九月、十一月は、一日未満か三十日を超えたら例外を投げる。そのほかの月は、一日未満か三十一日を超えたら例外を投げる」

ということをC#で書いたのだな。

   
ええ、まあ、そういうことになりますか。
   

複雑に考えたな。これがお前にとって自然な考え方なのか。これがお前には「分かりやすい」のか。

 大体、こんな複雑なコードを書いてしまっては、それが正しいかどうかをどうやって確かめるんだ!?

   
えっと、ロジックを目で追って……。
   

そんなので正しいかどうか分かるがやつがいたら、そいつにはテストもデバッグも必要ないな。

   

えっと、じゃあ動かしてみて、実際にいろいろと日付を入れてみて確かめる……。

   

かなりいろいろなパターンについて確かめる必要があるぞ。それを全部手入力か!? それに、コードというのは要求の変化に従ってどんどん変化するものだ。コードが変化するたびにそれをやるのか? 面倒くさすぎだ。

 その前に、そもそも「どのように動作するのが正しいのか」という仕様を決めたのか?

 ……まあ、お前とお前のチーム全員が、「仕様の妥当性が頭の中で判断できて、複雑なロジックも頭の中で考え出せて、それを書いてもバグを出さないし、今後の変化もすべて予測していてそれを盛り込めるスーパー・プログラマー」だったら、これでもよいかもしれんな。

   
それって皮肉ですよね……。
   

もちろんだ。

 お前の中の問題意識は多分こうだ。

「どうやって
 
作ろうか」

(つまり   “How”
という 問題意識)

これを「プチ・パラダイム・シフト」させるんだ。


“You have to let it all go. Free your mind.”
(既成概念を捨てろ! 心を解き放て)

こう考えろ。

「何を作ろうか」(つまり“What”という問題意識)

そして「作ると決めたものを作る」んだ。

   
はあ……、そういわれても……。
   
何度でもいう。シンプルに考えろ。

■■「シンプルさ」にコミットする演習 ― Dateクラスの仕様を決める「動く設計書」の作成■■

ここでキーになる考え方は「視点の転換」だ。まず何を作るか決めるんだ! ソース・コードを書く前に仕様を決めよう。

 それには、NUnitという「クラスの仕様を書くためのツール」を使う。NUnitの使い方は今日は割愛するが(*3)、参考までに挙げておくと、クラス仕様の書き方はこんな具合になる。

*3NUnit入門 Test Firstのススメ [NUnit 2.0対応版]」に詳しい。NUnitで実行できるテストを書くことにより、クラスの仕様を表すことができる。
 
[TestFixture]
public class Dateの作成時にどういう場合に例外が発生するかというと
{
  [Test][ExpectedException(typeof(ArgumentException))]
  public void 一年未満はエラーになる()
  {
    Date date = new Date(0, 1, 1);
  }

  [Test]
  public void 一月から十二月までならよいけれど()
  {
    Date date = new Date(2002,  1, 3);
    date      = new Date(2003, 12, 4);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void 一月未満はエラーだし()
  {
    Date date = new Date(2004, 0, 5);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void 十二月を超えた場合もエラーだ()
  {
    Date date1 = new Date(2005, 13, 6);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void 同様に一日未満はエラーだ()
  {
    Date date = new Date(2006, 3, 0);
  }

  [Test]
  public void 小の月の三十日までや大の月の三十一日までならよいけれど()
  {
    Date date = new Date(2007, 4, 30);
    date      = new Date(2008, 7, 31);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void 小の月の三十日を超えた場合はエラーで()
  {
    Date date = new Date(2009, 9, 31);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void 大の月だって三十一日を超えちゃったらエラーなのだ()
  {
    Date date = new Date(2010, 8, 32);
  }

  [Test]
  public void んでもって年が4で割り切れる年は原則うるう年なので二月二十九日は大丈夫で()
  {
    Date date = new Date(2004, 2, 29);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void うるう年なら二月三十日はアウト()
  {
    Date date = new Date(2004, 2, 30);
  }

  [Test][ExpectedException(typeof(ArgumentException))]
  public void ただし年が100で割り切れちゃったらうるう年じゃない()
  {
    Date date = new Date(2100, 2, 29);
  }

  [Test]
  public void でも年が400で割り切れたらうるう年()
  {
    Date date = new Date(2000, 2, 29);
  }
}
NUnitを使った場合の「Dateクラスの仕様」の記述例
   

なんだか、随分くだけた調子ですね。NUnitというツールを活用するためのDateクラスの仕様を書いたのですね。

   

そうだ。いいか。お前のものと似ているようだが違う。お前のは“How”だが、これは“What”だ。視点が違う。“How”はDateクラスを作る側の視点だ。“What”はDateクラスを使う側の視点だ。

 本来、クラスの仕様、すなわちクラス名やpublicなメソッドの名前やパラメータ、戻り値、例外、それからpublicなプロパティやイベントなどは、この視点で決定すべきなんだ。要するに、クラスが外部に公開するアプリケーション・プログラミング・インターフェイス(API)は、それを使用する側の視点で決める、ということだ。「どんなクラスが必要か?」を考えて、まずは「何を作るか」を決める。

 まあ実際には、こんなにクラス仕様を一挙にすべて書いてしまわずに、コーディングしながら少しずつ進めていくのだが、今日は紙面の都合により一気に書いてみた。

   
「紙面の都合」って何ですか?
   

知らん。

 これはDateクラスの仕様を決定づける「動く設計書」だ。「検証可能な設計書」といい換えてもいい。なんとNUnitを使うと、この仕様どおりに実際のDateクラスができているかどうか、検証を何度でも自動で行ってくれる。

   
すごいツールですね。
   

そう思うか。さて、上記のソース・コードから、メソッド名だけを読んでみると次のような具合だ。

「Dateの作成時にどういう場合に例外が発生するかというと:
一年未満はエラーになる。
一月から十二月までならよいけれど、
一月未満はエラーだし、
十二月を超えた場合もエラーだ。
同様に一日未満はエラーだ。
小の月の三十日までや大の月の三十一日までならよいけれど、
小の月の三十日を超えた場合はエラーで、
大の月だって三十一日を超えちゃったらエラーなのだ。
んでもって年が4で割り切れる年は原則うるう年なので二月二十九日は大丈夫で、
うるう年なら二月三十日はアウト。
ただし年が100で割り切れちゃったらうるう年じゃない。
でも年が400で割り切れたらうるう年」

   
なるほど。
   

この「Dateクラスの仕様」がそのまんまNUnitを使ったC#のコードになっているのが、先ほどのソース・コードなのだ。

 

 INDEX
  NAgileで始める実践アジャイル開発
  第2回 ソフトウェア開発をシンプルにする考え方のコツ
    1.“NAgile”って何? どうやる?
    2.NAgileにおけるシンプルさの極意“NSimplicity”
  3.「シンプルさ」にコミットする演習 ― シンプルに考えるための問題意識
    4.「シンプルさ」にコミットする演習 ― 実際のDateクラスのソース・コードの実装
    5.“Be Agile. That's my attitude.”という心構えを持とう!
 
インデックス・ページヘ  「NAgileで始める実践アジャイル開発」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間