[プログラム設計事始] プログラムの本質と、 インターフェース部分を分けるということ
学研国語大辞典 Ver.1.00 (1998) より
ことはじめ 【事始(め)】
① 初めて新しい仕事にとりかかること。 新しく物事をはじめること。 また、 新しい物事のはじまり。
「プログラム設計事始」 と題して、 何回続くかわかりませんが、 いくつか記事を書いていこうと思います。
想定している読者は、 Visual Studio を使って C# のプログラムが書けるレベルを前提としています。
◆ はじめに。 なぜ 「ソフトウェア設計事始」 ではないのか?
ソフトウェアを設計する ( = 作る ) という活動には、 大きく分けると 2つの段階があります。
・ なにを作るか ( what )
・ どうやって作るか ( how )
「なにを作るか ( what )」 には、 誰が何のために使うのかということも含みます。
いわゆる要件開発 ( 要件定義 ) からソフトウェアの外部設計 ( ソフトウェアを外からみたときの挙動の定義 ) あたりまでの活動が、 該当します。
ソフトウェアの開発を請け負った場合、 この活動はお客様のほうを向いて行います。 ( いくら良いアイディアだと思っても、 お客様がノーと言えば、 そんな仕様は無しです。 )
なにを作るかが固まってきたら、 「どうやって作るか ( how )」 という活動に入っていきます。
いわゆる内部設計から実装あたりまでの活動です。
この活動は、 コンピューターのほうを向いて行います。 ( いくら良いアイディアだと思っても、 コンピューターが受け付けてくれなければ、 そんな実装は無しです。 )
※ 私は、 さらに、 「正しく出来ているか」 ( テスト ) のことも視野に入れて、 「どうやって作るか ( how )」 を進めていくべきだと考えています。 一般的に、 いわゆる実装工程とテスト工程に掛かる費用は、 同じくらいかテストの方が大きいからです。
2つの段階のうち、 この記事では、 「どうやって作るか ( how )」 に焦点を当てて書いていきます。
一般的には、 ソフトウェアの内部設計、 という言い方をするのでしょう。 ですが、 たとえば 「メソッドの外部設計」 といったことを話題にするつもりです。 「ソフトウェアの内部設計」 に 「外部設計」 の話が出てくるというのも、 おかしなものです。
また、 「ソフトウェアの内部設計」 には、 実装 ( コーディング ) を含まないというニュアンスがあります。 しかし、 現代の実装 ( コーディング ) 作業には、 メソッドの内部設計が確実に含まれています。 おそらくは、 クラスの内部設計も含むでしょう。 それどころか、 クラス構成の設計まで含まれていることもザラです。
いわゆる 「ソフトウェアの内部設計」 からメソッドの内部設計 ( 実装 ) にいたるまでの範囲を意味する言葉が見つからないので、 この記事の中では 「プログラム設計」 という言葉を使うことにします。
なお、 サンプルコードのプログラミング言語としては C# を使います。
サンプルコードは、 可能な限り無償で利用できる開発環境で動かせるものにするつもりです。
※ サンプルコードを動かしてみるためには。
Visual Studio 2008 Express Edition SP1 の C# と Visual Web Developer は必須です。
その他に必要なものがあるときは、 その都度記載します。
◆ プログラムの本質と、 インターフェース部分を分けるということ
「どうやって作るか ( how )」 という意味での 「設計」 の基本は、 分割統治 ( divide and conquer ) です。
「ソフトウェアの内部設計」 を全部一度にまとめて考えることは、 きわめて困難です。 そこで、 より小さい部分々々に分割して ( divide ) 考えていき、 後からくっつけ合わせて全体を 「征服」 する ( conquer ) のです。 このやり方は、 プログラムに限りません。 機械の設計でも同じようにやっています。
ところで、 全体を分割するとき、 いっぺんに完全に分割してしまえばよいでしょうか?
プログラムで言えば、 外部設計書を見て、 いきなり全部のメソッドを数え上げることが出来るでしょうか?
全体がほどほどのサイズであったらなら、 それも可能でしょう。 普通はいきなりそんなことは出来ないので、 まず大雑把に分割し、 それぞれをまた分割し… と、 何回かに分けて分割を進めていきます。
※ 私は自動車会社に 10年近く居たことがあって、 そこでも設計をやっていました。
自動車の 「内部設計」 は、 まず、 ボディー / 内装 / エンジン / ミッション / 足回り / 電装 といった大きなブロックに分解し、 次に、 例えば内装は インパネ / シート / 内張り といった中くらいのブロックに分解し、 さらにシートは… と順に分解していって、 最後にボルト 1本、 金具 1枚といったレベルの設計に辿り着きます。
クルマ全体の外部設計が提示されたからといって、 いきなり運転席シートのレバーを覆う金具のカタチを決めたりすることなど、 やはり出来はしないのです。
前振りが長くなりました。
ようやく本題に入ります。 最初は、 プログラムを 2つに分割することからです。 プログラムの内部を 2つに分割するとしたら、 どのようにするのが良いでしょう?
まず、 お題を示しますので、 分割することは考えずにプログラムを書いてみてください。
【 お題 】 Hello, World !
プログラミングの最初は "Hello, World !" と決まっています (笑)
それだけでは面白くないので、 "World" の部分の文字は、 ユーザーが指定したものとします。 つまり、 「ユーザーが指定した文字列 {foo} に対して、 "Hello, {foo} !" と表示するプログラム」 を書いてください。
実際に、 コンソール / Windows Forms / ASP.NET / Silverlight の 4つの環境用に書いてみたコードを示します。
※ コード全体はこちら。 ⇒ HelloWorld_20090515.zip (26,545 バイト)
ソリューションファイル ( HelloWorld.sln ) を Visual Web Developer で開いてください。
※ Silverlight 用のプロジェクトを開くには、 Visual Studio 2008 SP1 用 Microsoft Silverlight 2 Tools が必要です。 こちらの記事を参照してください。 ⇒ はじめての Silverlight 2.0
・ コンソール用 HelloConsole
static void Main(string[] args)
{
if (args.Count() == 0)
{
Console.WriteLine("USAGE: " + System.Reflection.Assembly.GetEntryAssembly().GetName().Name + " {target name}");
}
else
{
Console.WriteLine("Hello, " + args[0] + " !");
}
Console.ReadKey();
}
・ WinForm 用 HelloWindowsForms
private void button1_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(this.textBox1.Text))
MessageBox.Show("Hello, " + this.textBox1.Text + " !");
}
protected void OnButtonClicked(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(this.TextBox1.Text))
{
this.Label1.Text = "Hello, " + this.TextBox1.Text + " !";
}
}
・ Silverlight 用 HelloSilverlight
// ほんの少しだけ UI に凝ってますprivate void OnDoButtonClicked(object sender, RoutedEventArgs e)
{
if (this.TargetName.Visibility == Visibility.Visible)
{
if (!string.IsNullOrEmpty(this.TargetName.Text))
{
this.TargetName.Visibility = Visibility.Collapsed;
this.Message.Text = "Hello, " + this.TargetName.Text + "!";
this.Message.Visibility = Visibility.Visible;
}
}
else
{
this.TargetName.Visibility = Visibility.Visible;
this.Message.Visibility = Visibility.Collapsed;
}
}
さて。 これら 4つのコードは、 かなり違ってますよね。
なぜ違うのでしょう?
ユーザーインターフェース ( UI ) に対するコードの書き方 ( お約束 ) が違うし、 さらには UI の挙動も変えているから、 ですね。
では、 共通する部分は?
"Hello, " + {文字列} + "!"
という、 文字列を連結しているコードが、 ( {文字列} の部分のコードは違いますが ) そっくりです。
ところで、 お題は、 「ユーザーが指定した文字列 {foo} に対して、 "Hello, {foo} !" と表示するプログラム」 でした。
この問題の本質は何でしょう?
人や UI などの要素を取り払って煎じ詰めれば、 「文字列 {foo} から、 "Hello, {foo} !" という文字列を作リ出す」 ことでしょう。
そして、 それは上のサンプルコードで、 共通なコードとして表現されている部分です。
すなわち、 こう言い換えることができます。
上の 4つのコードで共通する部分 → プログラムの本質
上の 4つのコードで異なる部分 → ユーザーインターフェース
ここで 「プログラムの本質」 と書きましたが、 一般的には 「ロジック」 という言葉で呼んでいるかと思います。
このロジック部分 ( プログラムの本質 ) だけでは、 ユーザーがそれを利用することができませんよね。 そこで、 ユーザーがロジックを使うために必要になるのが、 ユーザーインターフェース部分です。
ユーザーとプログラムの本質との間のインターフェース ( interface ~ 境界面 )、 繋ぎ合わせるものですね。
そして、 ロジック部分の設計にあたっては、 このプログラムの本質は何だろうと掘り下げていく方向に思考します。
インターフェース部分の設計にあたっては、 どうやったら双方を上手くすり合わせることができるだろうか、 と、 ロジックとは異なり、 いわば 「横方向に」 思考することになります。
設計するときに思考していく向きが異なるので、 これら 2つを分離することには、 それぞれの思考方向に専念できるというメリットがあります。
プログラム設計の最初は、 このようにして プログラムの本質 と インターフェース に分解するところから始めます。
なお、 インターフェースには、 ユーザーに対する部分だけでなく、 ファイルなどのコンピューター内にあるリソースに対するインターフェースや、 外部のサービスを利用するためのインターフェースなど、 様々なものがあります。
そうそう。 今回のお題の、 ロジック部分だけを取り出すと、 こんなメソッドになりますね。
public static string BuildMessage(string targetName)
{
return string.Format(null, "Hello, {0}!", targetName);
}
前のサンプルコードでは、 文字列を + 演算子を使って連結していましたが、 それでは効率がよろしくないので、 String クラスの Format() メソッドを使うようにしました。
次回の予定 ( …って、 予告なんてやって大丈夫だろうか f(^^; )
次は、 一番細かいレベルに飛んで、 メソッドを設計するときの話をする予定です。
| 固定リンク
「プログラミング」カテゴリの記事
- 【.NET / Win8.1 ストアアプリ】 HttpClient で TLS 1.1 / 1.2 に対応するには(2018.06.17)
- 【VS2017 15.7pv2】 XAML のランタイム ツールに 「ヒートマップ」 が増えた(2018.03.28)
- 【.NET Core】 プロジェクトを作ると 「project.assets.json が見つかりません」 エラー(2018.02.10)
- 【#UWP】 ビットマップの表示色を変える (Win2D.uwp 経由で Direct2D を使う)(2017.08.23)
- 【#UWP】 CompactOverlay モード: Picture in Picture というか、「最前面に表示」するウィンドウを作る(2017.08.16)
この記事へのコメントは終了しました。
コメント
どもです~
しばらく進むと、 話はテストファーストにつながる …かもしれない f(^^;
投稿: biac | 2009年5月16日 (土) 19時39分
楽しみにしてまっ!
投稿: 画伯 | 2009年5月16日 (土) 14時52分