.NET プログラミング技術 オブジェクト指向プログラミング言語として .NET トレーニング テキスト Rev.2004.9.1 Copyright© 2003 OSK Co., LTD. 目次 Session1 テスティング・フレームワークを利用する ........................................................1 Step 1 テスティングフレームワークをインストールする ................................................................................................ 1 Step 2 新規プロジェクトを作成する ...................................................................................................................................... 2 Step 3 テストプロジェクトを追加する .................................................................................................................................... 4 Step 4 クラスとテストクラスを作成する................................................................................................................................ 7 Step 5 他のクラスを利用して、新しいクラスを作成する.............................................................................................13 Step 6 複数のクラスの共通部分をクラスにまとめる ...................................................................................................17 Step 7 親クラスで雛型メソッドを作成する ........................................................................................................................21 Session2 オブジェクト指向プログラミングを行う ..........................................................23 Step 1 ポリモーフィズムを利用してブロックセット作成する.......................................................................................23 Step 2 インスタンスの作成を利用から切り離す ............................................................................................................28 Step 3 インターフェイスを導入する .....................................................................................................................................32 Step 4 委譲を実装する ............................................................................................................................................................35 Session3 Windows アプリケーションを作成する .........................................................38 Step 1 新規プロジェクトを作成する ....................................................................................................................................38 Step 2 フォームを作成する.....................................................................................................................................................40 Step 3 メニューを作成する .....................................................................................................................................................43 Step 4 ユーザインターフェイスをテストする.....................................................................................................................43 Step 5 フォームウィンドウの処理を実装する..................................................................................................................52 Session4 Web アプリケーションを作成する ................................................................56 Step 1 仮想フォルダを作成する...........................................................................................................................................56 Step 2 プロジェクトを作成する ..............................................................................................................................................57 Step 3 Web フォームの処理を実装する ............................................................................................................................60 Step 4 既存クラスに機能拡張する......................................................................................................................................64 Step 5 オブジェクトを再構築する .........................................................................................................................................66 付録 1 NUnit リファレンス.....................................................................................67 A) 属性 ...................................................................................................................................................................................67 B) Assert クラス..................................................................................................................................................................67 C) Assertion クラス............................................................................................................................................................70 Copyright© 2003 OSK Co., LTD. Session1 テスティング・フレームワークを利用する Overview Visual Studio.NET(以下 VS.NET)は、.NET アプリケーションを開発するための統合開発環境です。非常に強力 で、Windows アプリケーションから Web アプリケーション、XML Web サービス、大規模な分散システムまで様々な アプリケーション開発をサポートしています。VS.NET に、単体テストを標準化し、効率的な開発を支援するテステ ィング・フレームワークである NUnit を連動させることで、快適な開発を行うことができます。今回対象としている システムは、一度作ったら使い捨て的なものや個人の趣味で開発しているシステムを対象にしているのではなく、一定 の期間において機能拡張やユーザ要求の対応などを行うシステムです。 この Session では、テスティング・フレームワークを利用するプロジェクトの作成手順や VS.NET のソリューショ ン構成について、解説します。 .NET では、オブジェクト指向を全面的に作用しています。したがって、オブジェクト指向プログラミングをある程 度行うことが要求されますし、オブジェクト指向プログラミングを活用することで、効率的なプログラム作成を行うこ とができます。この Session では、オブジェクト指向プログラミングの解説を具体的にプログラムを作成する方法で行 います。 この Session では、積み木システムを例題として、作業を進めます。 Goal NUnit が利用できる テストファーストを実感する オブジェクト指向プログラミングを実感する クラスの作成を行う、クラスの継承を行う Step 1 テスティングフレームワークをインストールする ① NUnit を入手する NUnit Ver2.1.4 をサイト、「http://sourceforge.net/projects/nunit/」からダウンロードします。 ダウンロードするファイルは、 「NUnit-V2.1.4.msi」です。 (2004 年 8 月時点では、フリーソフトです。また、ねん のためウィルスチェックを行うことをお勧めします) ② インストールを実行する ファイル名でお分かりだと思いますが、Windows のインストールパッケージになっています。WindowsXP であれ ば、クリックし、インストール・ウィザードの指示に従うだけで、インストールを行うことができます。インスト ールを行うと、実行モジュールと単体テスト用のフレームワーク、各種ドキュメント、ソースファイル、サンプル などがコピーされます。 Hints NUnit をデフォルトの設定でインストールを行うと、OSをインストールした ドライブの「¥Program Files」ディレクトリに、右のようなディレクトリ構成で インストールが行われます。 1 Copyright© 2003 OSK Co., LTD. Step 2 新規プロジェクトを作成する ① ルートディレクトリを作成する 「エクスプローラ」などを使用してルートディレクトリを作成します。具体的には、 「¥dotNetSeminar¥Exercises」 内に「BuildingBlock」作成します。 ② 新規プロジェクトを作成する 「ファイル」メニューの「新規作成」から「プロジェクト」を選択して、新規プロジェクトの作成を開始します。 「ク ラスライブラリ」テンプレートを使用して、次の設定でプロジェクトを作成します。 設定項目 設定内容 プロジェクトの種類 各言語のプロジェクト テンプレート クラスライプラリ プロジェクト名 Core 場所 ¥dotNetSeminar¥Exercises¥ BuildingBlock 新しいソリューション Source VS.NET では、最上位にソリュー ションがあります。その下には、 複数のプロジェクトを管理するこ とができます。プロジェクトには、 1つのディレクトリや仮想ディレ クトリ(Web アプリケーションの 場合)に割り当てられます。また、 新規作成で指定した、プロジェク トの種類やテンプレートは、作成 した後に変更することはできませ ん。たとえば、Basic のつもりで 作成したが、C#であったなどの場 合は、作り直しが必要です。 ③ ソリューション名を変更する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右 ボタンでコンテキストメニューを表示し、名前の変更を選択します。プロ ジェクト名を「BuildingBlock」に変更します。 この作業は、ソリューション名とディレクトリ名を異なったものにするた めです。 ④ ディレクトリを確認する デフォルトで作成される Class1.cs(VB の場合は Class1.vb)は削除します。その結果、 デ ィ レ ク ト リ 構 造 は 右 の よ う に な り ま す 。 Source デ ィ レ ク ト リ 配 下 に あ る 、 「BuildingBlock.sln」ファイルがソリューションファイルです。また、Core ディレクト リ配下にある「Core.csproj」(VB の場合は「Core.vbproj」)ファイルがプロジェクトフ ァイルです。 2 Copyright© 2003 OSK Co., LTD. ⑤ プロジェクトのプロパティを設定する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで Core プロジェクトのコンテキス トメニューを表示し、プロパティを選択します。以下の設定を変更します。 C#の場合 設定項目 アセンブリ名 既定の名前空間 設定内容 OSK.BuildingBlock.Core OSK.BuildingBlock.Core VB .NET の場合 設定項目 アセンブリ名 ルート名前空間 設定内容 OSK.BuildingBlock.Core [空白] Hints ソリューションはできるだけ1つで作業することが効率的です。しかし、大規模分散システムなどでは、複数ソリ ューションとする必要があります。マイクロソフトの MSDN の「Visual Studio .NET と Visual SourceSafe を使用 したチーム開発」の中の「第 3 章 ソリューションとプロジェクトの構造化」が参考になると思います。 3 Copyright© 2003 OSK Co., LTD. Step 3 テストプロジェクトを追加する ① テスト用のプロジェクトを追加する 「ファイル」メニューの「プロジェクトの追加」から「新規プロジェクト」を選択して、新規プロジェクトの作成 を開始します。 「Visula Basic プロジェクト」の「クラスライブラリ」テンプレートを使用して、次の設定でプロジェクトを作成 します。 設定項目 プロジェクトの種類 テンプレート プロジェクト名 場所 設定内容 各言語のプロジェクト クラスライプラリ Tests ¥dotNetSeminar¥Exercises¥ BuildingBlock¥Source デフォルトの Class1.cs(VB の場 合は Class1.vb)は、削除します。 ② プロジェクトのプロパティを設定する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで Tests プロジェクトのコンテキ ストメニューを表示し、プロパティを選択します。以下の設定を変更します。 NUnit を使用して、単体テストを行う場合は、デバッグ用の外部プログラムとして NUnit が提供している EXE を 使用します。ここでは、開発中に利用するために用意されている「nunit-gui.exe」を使用します。 C#の場合 プロパティ種別 全般 全般 デバッグ デバッグ デバッグ 設定項目 アセンブリ名 既定の名前空間 デバッグモード スタートアプリケーション コマンドライン引数 設定内容 OSK.BuildingBlock.Tests OSK.BuildingBlock.Tests プログラム ¥Program Files¥NUnit V2.1¥bin¥nunit-gui.exe OSK.BuildingBlock.Tests.dll 4 Copyright© 2003 OSK Co., LTD. デバッグモードをプログ ラムに変更後、適用を押し てから、スタートアプリケ ーションの設定を行いま す。 デバッグモードを設 定したら適用を押す VB .NET の場合 プロパティ種別 全般 全般 デバッグ デバッグ 設定項目 アセンブリ名 ルート名前空間 外部プログラムの開始 コマンドライン引数 設定内容 OSK.BuildingBlock.Tests [空白] ¥Program Files¥NUnit V2.1¥bin¥nunit-gui.exe OSK.BuildingBlock.Tests.dll 5 Copyright© 2003 OSK Co., LTD. ③ ビルドと実行を行う 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで Tests プロジェクトのコンテキ ストメニューを表示し、「スタートアップ プロジェクトに設定」を選択します。 「ビルド」メニューの「ソリューションのビルド」を選択して、ビルドを行い、エラーが発生しないことを確認し ます。 「デバッグ」メニューの「開始」を選択して、テスト実行を行います。以下のようなウィンドウが表示されます。 ここをクリック その後、Run ボタンを押すと、テストリストの表示が黄色に変わります。また、Tests Not Run タグを選択すると、 テストが無いというメッセージが表示されます。 まだ黄色 Reason: Has no TestFixtures 6 Copyright© 2003 OSK Co., LTD. Step 4 クラスとテストクラスを作成する 複 数 の機 能 から構 築 されるサービス(ここでは、一 連 の関 連 した機 能 によって提 供 される高 度 な機 能 をサービスと 呼 びます)を提 供 するためには、.NET では、クラスという概 念 を導 入 しています。クラスは、機 能 と情 報 をあわせ持 っ ています。このことにより、クラスの行 う処 理 が複 雑 な機 能 であっても、クラスを利 用 する側 は複 雑 な処 理 を意 識 する ことなく、サービスを受 けることができます。 ① 名前空間を決定する .NET では、クラスを階層的にグループ化して管理を行っています。この階層のことを名前空間と呼びます。独自に クラスを作成する場合、独自の名前空間を用意する必要があります。名前空間によって、クラスが識別されますの で、以下のような形式作成します。 企業名(組織名).テクノロジー名.機能名 この Session のサンプルでは、名前空間を「OSK. BuildingBlock.プロジェクト名」とします。 ② プロジェクト間の参照設定を行う プロジェクト間の参照設定を行います。今回は、「Tests」プロジェクトで 「Core」プロジェクトの参照設定を行います。 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボ タンで Tests プロジェクトの参照設定コンテキストメニューを表示し、「参 照」を選択します。 「参照の追加」ダイアログ ボックスの「プロジェク ト」タグを選択して、選択 ボタンを押します。選択さ れたコンポーネントのリ ストに追加されます。 Core を選択して、ここをクリック 7 Copyright© 2003 OSK Co., LTD. 次に、.NET タグで NUnit のクラスライブラリ「nunit.framework」を参照します。 上記の方法以外に、参照ボタンを押して、DLL を直接していすることもできます。 ディレクトリ位置 ファイル名 ③ 内容 ¥Program Files¥NUnit V2.1¥bin nunit.framework.dll クラスを作成する 丸いブロックを Circle という名前で作成します。 「ソリューション エクスプローラ」ウィンドウを 表示して、マウスの右ボタンで Core プロジェクトの コンテキストメニューを表示し、 「追加」の「新しい 項目の追加」を選択します。 表示された新しい項目の追加ダイアログボックスに以下の設定を行って、クラスを作成します。 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 Circle.cs(VB の場合は、Circle.vb) 8 Copyright© 2003 OSK Co., LTD. 指定したファイル名がクラス 名となって、ソースファイルが 作成されます。 クラス名とファイル名が同じ 必要はありませんが、わかりや すくするために、同じにするこ とをお勧めします。 ④ クラスの雛形を実装する 名前空間を指定して、Circle クラスのソースを作成します。クラスは、コンストラクタおよびプロパティ、メソッ ドを持っています。各実装はまだ行いません。 C#の場合 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: namespace OSK.BuildingBlock.Core { public class Circle { public Circle(double diameter) { } public double Radius { get { return 0.0; } } public string Nickname { get { return ""; } set { } } public double CalculateArea() { return 0.0; } } } 9 Copyright© 2003 OSK Co., LTD. VB .NET の場合 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: Namespace OSK.BuildingBlock.Core Public Class Circle Public Sub New(ByVal diameter As Double) End Sub Public ReadOnly Property Radius() As Double Get Return 0.0 End Get End Property Public Property Nickname () As String Get Return “” End Get Set(ByVal Value As String) End Set End Property Public Function CalculateArea() As Double Return 0.0 End Function End Class End Namespace Hints 本来のテストファーストでは、クラスよりテストを先に作成しますが、VS.NET ではメソッドなどを空の状態で実 装する方法も有効です。 Diameter は直径、Radius は半径。 ⑤ テストクラスを作成する Circle クラスをテストするクラスを CircleTestCase という名前で作成します。 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで Tests プロジェクトのコンテキ ストメニューを表示し、 「追加」の「新しい項目の追加」を選択します。表示された新しい項目の追加ダイアログボ ックスに以下の設定を行って、クラスを作成します。 設定項目 カテゴリ テンプレート ファイル名 ⑥ 設定内容 コード(選択しなくても、特に問題はない) クラス CircleTestCase.cs(VB の場合は CircleTestCase.vb) テストクラスを実装する 今回、CircleTestCase では、以下の項目について、テストを作成します。 ・ コンストラクタのテスト 直径を指定してコンストラクタ呼び出し、インスタンスが作成されたことを確認します。また、半径が正しく取得できること を確認します。 ・ プロパティの設定と取得のテスト ・ 面積計算のテスト クラスに属性として、TestFixture を付けます。同様に、テストメソッドにも属性として Test を付けます。最後に、 テストプログラムを作成します。テストメソッドは、必ず以下の書式にする必要があります。 C#の場合 public void テストメソッド名(引数なし) VB .NET の場合 Public Sub テストメソッド名(引数なし) 以下のソースは、最終的な CircleTest のソースです。 10 Copyright© 2003 OSK Co., LTD. C#の場合 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class CircleTestCase { [Test] public void ConstructorTest() { Circle circleObj; circleObj = new Circle(10.0); Assert.IsNotNull(circleObj,"インスタンスが作成された"); Assert.AreEqual(5.0, circleObj.Radius,"半径の確認で NG"); } [Test] public void PropertyTest() { Circle circleObj; circleObj = new Circle(10.0); circleObj.Nickname = "まる"; Assert.AreEqual("まる", circleObj.Nickname,"プロパティのチェック"); } [Test] public void CalculateArea() { Circle circleObj; circleObj = new Circle(10.0); Assert.AreEqual(5.0 * 5.0 * 3.14, circleObj.CalculateArea(),"面積計算"); } } } VB .NET の場合 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class CircleTestCase <Test()> _ Public Sub ConstructorTest() Dim circleObj As Circle circleObj = New Circle(10.0) Assert.IsNotNull(circleObj,"インスタンスが作成された") Assert.AreEqual(5.0, circleObj.Radius,"半径の確認で NG") End Sub <Test()> _ Public Sub PropertyTest() Dim circleObj As Circle circleObj = New Circle(10.0) circleObj.Nickname = "まる" Assert.AreEqual("まる", circleObj.Nickname,"プロパティのチェック") End Sub <Test()> _ Public Sub CalculateArea() Dim circleObj As Circle circleObj = New Circle(10.0) Assert.AreEqual(5.0 * 5.0 * 3.14, circleObj.CalculateArea(),"面積計算") End Sub End Class End Namespace Hints 実際に作成するときは、1つテストを作成したら、その部分の実装を行います。また、1つテストを作成したら、 その部分のテストを行います。このように、作業を進める方法が有効です。 11 Copyright© 2003 OSK Co., LTD. ⑦ テストを実行する テストプログラムを実装したら、テス トを実行します。NUnit のウィンドウ が表示されたら、Run ボタンを押して、 テストを実行します。最初は、Circle クラスの何も実装されていないので、 右のようにエラー(赤いバー)が表示 されます。このエラーがなくなり、緑 色のバーになるまで、実装を行うこと になります。 C#の場合 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: using System; namespace OSK.BuildingBlock.Core { public class Circle { private const double PI = 3.14; private double diameterValue; public Circle(double diameter) { diameterValue = diameter; } public double Radius { get { return diameterValue / 2.0; } } private string nicknameValue; public string Nickname { get { return nicknameValue; } set { nicknameValue = value; } } public double CalculateArea() { return Radius * Radius * PI; } } } Hints C#でプロパティを定義するためには、以下の形式でおこないます。 public 型名 プロパティ名 { } プロパティを設定する場合は、必ず value という変数が使用されます。 12 Copyright© 2003 OSK Co., LTD. VB .NET の場合 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: Namespace OSK.BuildingBlock.Core Public Class Circle Private Const PI As Double = 3.14 Private diameterValue As Double Public Sub New(ByVal diameter As Double) diameterValue = diameter End Sub Public ReadOnly Property Radius() As Double Get Return diameterValue / 2.0 End Get End Property Private nicknameValue As String Public Property Nickname() As String Get Return nicknameValue End Get Set(ByVal Value As String) nicknameValue = Value End Set End Property Public Function CalculateArea() As Double Return Radius * Radius * PI End Function End Class End Namespace Step 5 他のクラスを利用して、新しいクラスを作成する 既 に作 成 されたクラスを部 品 として利 用 し、新 しいクラスを作 成 することができます。この様 に、他 のクラスの一 部 分 となるクラスを部 分 クラスと呼 びます。部 分 クラスは、全 体 クラスのインスタンスが作 成 されたときに、部 分 クラスのイン スタンスも作 成 され、全 体 クラスのインスタンスが破 棄 されるときには、同 時 に破 棄 されるのが一 般 的 です。したがって、 部 分 クラスのインスタンスの生 存 期 間 は、全 体 クラスのインスタンスの生 存 期 間 に含 まれます。 ① クラスを作成する Circle クラスを2つ利用して、雪だるまクラスの Snowman クラスを新規作成し ます。 雪だるまは、高さを2対3にした2つの円を組み合わせたものとします。 2 3 設定項目 カテゴリ テンプレート ファイル名 設定内容 コード(選択しなくても、特に問題はない) クラス Snowman.cs(VB の場合は、Snowman.vb) 13 Copyright© 2003 OSK Co., LTD. Snowman クラスのコンストラクタには、雪だるまの高さを引数に渡します。また、プロパティとして Nickname、 メソッドとして、面積計算を用意します。 C#の場合 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: using System; namespace OSK.BuildingBlock.Core { public class Snowman { public Snowman(double height) { } public string Nickname { get { return ""; } set { } } public double CalculateArea() { return 0.0; } } } VB .NET の場合 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: ② Namespace OSK.BuildingBlock.Core Public Class Snowman Public Sub New(ByVal height As Double) End Sub Public Property Nickname() As String Get Return "" End Get Set(ByVal Value As String) End Set End Property Public Function CalculateArea() As Double Return 0.0 End Function End Class End Namespace テストクラスに初期化処理と終了処理を作成する Snowman クラスのテスト用クラスとして、SnowmanTestCase を新規作成します。 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 SnowmanTestCase.vb テストクラスのテスト用のメソッドでは、テストを行うクラスの作成を行うことが良くあります。この処理をメソ ッド単位で行うと、プログラムが重複してしまうので、NUnit では、初期化処理と終了処理を SetUp 属性と TearDown 属性を指定することで、共通化することができます。この初期化処理と終了処理は、テストごとに呼び 出されます。 14 Copyright© 2003 OSK Co., LTD. 今回、SnowmanTestCase では、以下の項目について、テストを作成します。 ・ プロパティの設定と取得のテスト ・ 面積計算のテスト 高さを指定して作成したインスタンスの面積が正しく計算されることを確認します。 最終的なテストは、以下のようになります。 C#の場合 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class SnowmanTestCase { private Snowman target; [SetUp] public void Setup() { target = new Snowman(20.0); } [TearDown] public void TearDown() { target = null; } [Test] public void PropertyTest() { target.Nickname = "雪だるま"; Assert.AreEqual("雪だるま", target.Nickname,"プロパティのチェック"); } [Test] public void CalculateArea() { Assert.AreEqual(4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14, target.CalculateArea(),"面積計算"); } } } VB .NET の場合 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class SnowmanTestCase Private targetObj As Snowman <SetUp()> _ Public Sub Setup() targetObj = New Snowman(20.0) End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub <Test()> _ Public Sub PropertyTest() targetObj.Nickname = "雪だるま" Assert.AreEqual("雪だるま", targetObj.Nickname,"プロパティのチェック") End Sub <Test()> _ Public Sub CalculateArea() Assert.AreEqual(4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14, targetObj.CalculateArea(), _ "面積計算") End Class End Namespace 15 Copyright© 2003 OSK Co., LTD. ③ クラスを完成させる テストが全て通るように Sonwman クラスを実装します。最終的なクラスは以下のようになります。 C#の場合 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: using System; namespace OSK.BuildingBlock.Core { public class Snowman { private Circle headObj; private Circle bodyObj; public Snowman(double height) { headObj = new Circle(height * 2 / 5); bodyObj = new Circle(height * 3 / 5); } private string nicknameValue; public string Nickname { get { return nicknameValue; } set { nicknameValue = value; } } public double CalculateArea() { return headObj.CalculateArea() + bodyObj.CalculateArea(); } } } VB .NET の場合 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: Namespace OSK.BuildingBlock.Core Public Class Snowman Private headObj As Circle Private bodyObj As Circle Public Sub New(ByVal height As Double) headObj = New Circle(height * 2 / 5) bodyObj = New Circle(height * 3 / 5) End Sub Private nicknameValue As String Public Property Nickname() As String Get Return nicknameValue End Get Set(ByVal Value As String) nicknameValue = Value End Set End Property Public Function CalculateArea() As Double Return headObj.CalculateArea() + bodyObj.CalculateArea() End Function End Class End Namespace 16 Copyright© 2003 OSK Co., LTD. Step 6 複数のクラスの共通部分をクラスにまとめる クラスを複 数 作 成 すると、複 数 のクラス間 で、まったく同 じ部 分 を持 っていることがあります。このまま同 じプログラム を複 数 の場 所 に放 置 していると、同 じ修 正 を何 十 回 と繰 り返 すことになったり、またその作 業 でバグを混 入 させてし まったりと、保 守 性 が著 しく低 下 してしまいます。 この様 な場 合 、オブジェクト指 向 プログラミングでは、共 通 部 分 を1つのクラスにして、各 クラスで継 承 させる方 法 で、 共 通 部 分 を1つにまとめることができます。この様 な継 承 のことを汎 化 と呼 びます。共 通 部 分 のクラスを親 クラス、各 ク ラスを子 クラスと呼 びます。親 クラスの名 前 は、一 般 に抽 象 的 な名 前 や総 称 的 な名 前 を付 けます。例 えば、具 象 的 な円 ブロックや長 方 形 ブロックといったもの、親 クラスはブロックという総 称 的 な名 前 にします。 ① クラスを作成する 様々なブロックの親クラスとなる、Block クラスを新規作成します。 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 Block.cs(VB の場合は Block.vb) Block クラスは、プロパティとして Nickname を持ちます。このクラスは、他のクラスに継承してもらうことを前 提にしていますので、クラスの種別として「abstract」(VB の場合は「MustInherit」)を指定します。 最終的な Block クラスは、以下のようになります。 C#の場合 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: using System; namespace OSK.BuildingBlock.Core { public abstract class Block { private string nicknameValue; public string Nickname { get { return nicknameValue; } set { nicknameValue = value; } } } } VB .NET の場合 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: Namespace OSK.BuildingBlock.Core Public MustInherit Class Block Private nicknameValue As String Public Property Nickname() As String Get Return nicknameValue End Get Set(ByVal Value As String) nicknameValue = Value End Set End Property End Class End Namespace 17 Copyright© 2003 OSK Co., LTD. ② 既存クラスを変更する 親クラスを作成したら、今まで作成した Circle クラスと Snowman クラスに継承を設定し、共通部分を削除します。 C#の場合 C#での継承は、クラス定義の後に、 「:」を記述して、その後に親クラス名を記述します。その後、プロパティ Nickname に関する部分を削除します。ビルドを行ってエラーが発生しなければ、変更は終了です。 359: 360: 361: 362: namespace OSK.BuildingBlock.Core { public class Circle : Block { VB .NET の場合 VB での継承は、クラス定義の後に、キーワード「Inherits」を記述して、その後に親クラス名を記述します。その 後、プロパティ Nickname に関する部分を削除します。ビルドを行ってエラーが発生しなければ、変更は終了です。 363: 364: 365: ③ Namespace OSK.BuildingBlock.Core Public Class Circle Inherits Block テストを行う 既に作成してある Circle のテストケースを動作し、クラスの構造を変えても、テストが正常に動作することを確認 します。Snowman クラスも同様に作業を行います。 Points この Step のように、自動テスト環境があることで、勇気を持ってクラスの構造を最適化することができます。もし、 手作業で単体テストを行う環境では最適化は行えず、コードはどんどんスパゲッティ状態に退化してしまいます。 ④ 親クラスを利用して、新しいクラスを作成する 親クラスを利用して、四角形クラスを作成します。このときも、今までの手順と同じです。 C#の場合 Rectangule クラスは以下のように作成します。 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: using System; namespace OSK.BuildingBlock.Core { public class Rectangule : Block { public Rectangule(double height, double width) { } public double CalculateArea() { return 0.0; } } } 18 Copyright© 2003 OSK Co., LTD. RectanguleTestCase クラスは以下のようになります。 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class RectanguleTestCase { private Rectangule target; [SetUp] public void Setup() { target = new Rectangule(5.0, 10.0); } [TearDown] public void TearDown() { target = null; } [Test] public void PropertyTest() { target.Nickname = "しかく"; Assert.AreEqual("しかく", target.Nickname,"プロパティのチェック"); } [Test] public void CalculateArea() { Assert.AreEqual( 5.0 * 10.0, target.CalculateArea(),"面積計算"); } } } テスト実行を行うと、一部のテストが正常に終了します。これは、親クラスが既に実装しているからです。 最終的な Rectangule クラスは以下のようになります。 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: using System; namespace OSK.BuildingBlock.Core { public class Rectangule : Block { private double heightValue; private double widthValue; public Rectangule(double height, double width) { heightValue = height; widthValue = width; } public double CalculateArea() { return heightValue * widthValue; } } } 19 Copyright© 2003 OSK Co., LTD. VB .NET の場合 Rectangule クラスは以下のようになります。 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: Namespace OSK.BuildingBlock.Core Public Class Rectangule Inherits Block Private heightValue As Double Private widthValue As Double Public Sub New(ByVal height As Double, _ ByVal width As Double) heightValue = height widthValue = width End Sub Public Function CalculateArea() As Double Return heightValue * widthValue End Function End Class End Namespace RectanguleTestCase クラスは以下のようになります。 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class RectanguleTestCase Private targetObj As Rectangule <SetUp()> _ Public Sub Setup() targetObj = New Rectangule(5.0, 10.0) End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub <Test()> _ Public Sub PropertyTest() targetObj.Nickname = "しかく" Assert.AreEqual("しかく", targetObj.Nickname, _ "プロパティのチェック") End Sub <Test()> _ Public Sub CalculateArea() Assert.AreEqual(5.0 * 10.0, targetObj.CalculateArea(), _ "面積計算") End Sub End Class End Namespace 20 Copyright© 2003 OSK Co., LTD. Step 7 親クラスで雛型メソッドを作成する Step6 で、クラスの共 通 部 分 を親 クラスとしてまとめましたが、これだけでは今 までの共 通 モジュールとそれほど変 わ りません。例 えて言 えば、完 成 品 のパソコンのようなものです。これでは、必 ずしも、自 分 にあったパソコンではないこ とも多 く、適 応 範 囲 が限 定 されてしまいます。しかし、すべてを自 分 で組 み立 てるのも大 変 です。そこで登 場 するの が、カスタムメイドのパソコンです。個 別 に変 更 可 能 な部 分 をあらかじめ限 定 して用 意 することで、適 応 範 囲 を広 げる ことができます。 .NET では、あらかじめ変 更 部 分 を親 クラスでメソッドなどとして用 意 することができます。用 意 したメソッドとには、 C#では abstract(VB の場 合 は MustOverride)を宣 言 します。これで、このメソッドは、必 ず子 クラスで定 義 することを 義 務 付 けることになります。 ① カスタマイズポイントとなるメソッドを定義する カスタマイズポイントとなるメソッドとは、実際のプログラムは子クラスで実装し、型だけを親クラスで定義する ものです。今回のシステムでは、ブロックの面積の 10 倍を重さとします。したがって、計算式は、重さ=面積 * 10 となります。しかし、面積の計算方法は各ブロックの形によって異なります。そこで、メソッドの型だけを親クラ スで定義します。 C#の場合 475: public abstract double CalculateArea(); VB .NET の場合 476: ② Public MustOverride Function CalculateArea() As Double 各クラスのカスタマイズポイントを差し替えるメソッドを定義する カスタマイズポイントとなるメソッドを各子クラスで実装します。今回は、既に実装されていますので、キーワー ド「Overrides」をメソッドに付けます。そして、ビルドを行い、テストを実行します。 C#の場合 477: public override double CalculateArea() VB .NET の場合 478: Public Overrides Function CalculateArea() As Double 21 Copyright© 2003 OSK Co., LTD. ③ 変更部分を含んだメソッドを定義する Block クラスに、重さを計算するメソッドを定義します。その後、各クラスに重さをテストするテストメソッドを 追加し、テストが成功することを確認します。 C#の場合 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: using System; namespace OSK.BuildingBlock.Core { public abstract class Block { private string nicknameValue; public string Nickname { get { return nicknameValue; } set { nicknameValue = value; } } public abstract double CalculateArea(); public double CalculateWeight() { return CalculateArea() * 10.0; } } } VB .NET の場合 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: Namespace OSK.BuildingBlock.Core Public MustInherit Class Block Private nicknameValue As String Public Property Nickname() As String Get Return nicknameValue End Get Set(ByVal Value As String) nicknameValue = Value End Set End Property Public MustOverride Function CalculateArea() As Double Public Function CalculateWeight () As Double Return CalculateArea() * 10.0 End Function End Class End Namespace 22 Copyright© 2003 OSK Co., LTD. Session2 オブジェクト指向プログラミングを行う Overview .NET では、効率的な開発を行うために、オブジェクト指向技術を全面的に採用しています。このセッションでは、 オブジェクト指向技術で、難しいと言われているポリモーフィズムなどに焦点を当てて説明を行います。この Session でも、オブジェクト指向プログラミングの解説を具体的にプログラムを作成する方法で行います。 この Session でも、引き続き、積み木システムを例題として、作業を進めます。 Goal オブジェクト指向のポリモーフィズムを実感する インターフェイスベースのプログラミングを実感する 委譲を実感する Step 1 ポリモーフィズムを利用してブロックセット作成する 複 数 のブロックをブロックセットとして、まとめて管 理 できるクラスを作 成 する場 合 、セットへいろいろなブロックを追 加 するメソッドを用 意 する必 要 があります。今 までの VB プログラムであれば、AddCirlce や AddSnowman などのよう なに複 数 のメソッドを用 意 することがありました。各 ブロックをブロックという抽 象 レベルで扱 うことで、Add(ブロック)とい うひとつのメソッドで複 数 のクラスのインスタンスを同 じように操 作 することができるようになります。 ① クラスを作成する ブロックセットの BlockSet クラスを作成します。 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 BlockSet.cs(VBでは BlockSet.vb) 今回このクラスには、ブロックを追加するメソッドを用意します。また、追加されたブロックの面積と重さの合計 を計算するメソッドを用意します。更に、プロパティとして Nickname を用意します。実は、ブロックのセットも、 ブロックとして扱うことができます。したがって、BlockSet も Block を継承させることにします。 クラスの最初のソースは、以下のようになります。 C#の場合 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: using System; namespace OSK.BuildingBlock.Core { public class BlockSet : Block { public void Add( Block newBlock ) { } public override double CalculateArea() { return 0.0; } } } 23 Copyright© 2003 OSK Co., LTD. VB .NET の場合 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: ② Namespace OSK.BuildingBlock.Core Public Class BlockSet Inherits Block Public Sub Add(ByRef newBlock As Block) End Sub Public Overrides Function CalculateArea() As Double Return 0.0 End Function End Class End Namespace テストクラスを作成する BlockSet クラスのテスト用クラスとして、BlockSetTestCase を作成します。 ブロックを追加して、総面積と総重量を計算するテストを行います。また、ブロックがひとつもない場合のテスト も用意します。 C#の場合 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class BlockSetTestCase { private BlockSet target; [SetUp] public void Setup() { target = new BlockSet(); } [TearDown] public void TearDown() { target = null; } [Test] public void PropertyTest() { target.Nickname = "ブロックセット"; Assert.AreEqual("ブロックセット", target.Nickname, "プロパティのチェック"); } [Test] public void NoBlockTest() { Assert.AreEqual(0, target.CalculateArea(),"面積計算"); Assert.AreEqual(0, target.CalculateWeight(),"重さ計算"); } [Test] public void OneBlockTest() { double setArea; target.Add( new Circle(10.0) ); setArea = 5.0 * 5.0 * 3.14; Assert.AreEqual(setArea, target.CalculateArea(),"面積計算"); Assert.AreEqual(setArea * 10.0, target.CalculateWeight(),"重さ計算"); } 24 Copyright© 2003 OSK Co., LTD. 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: [Test] public void AnyBlockTest() { double setArea; target.Add( new Circle(10.0) ); setArea = 5.0 * 5.0 * 3.14; target.Add( new Snowman(20.0) ); setArea += 4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14; target.Add( new Rectangule(5.0, 10.0) ); setArea += 5.0 * 10.0; Assert.AreEqual(setArea, target.CalculateArea(),"面積計算"); Assert.AreEqual(setArea * 10.0, target.CalculateWeight(),"重さ計算"); } } } VB .NET の場合 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class BlockSetTestCase Private targetObj As BlockSet <SetUp()> _ Public Sub Setup() targetObj = New BlockSet() End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub <Test()> _ Public Sub PropertyTest() targetObj.Nickname = "ブロックセット" Assert.AreEqual("ブロックセット", targetObj.Nickname, "プロパティのチェック") End Sub <Test()> _ Public Sub NoBlock() Assert.AreEqual(0, targetObj.CalculateArea(),"面積計算") Assert.AreEqual(0, targetObj.CalculateWeight (),"重さ計算") End Sub <Test()> _ Public Sub OneBlock() Dim setArea As Double targetObj.Add(New Circle(10.0)) setArea = 5.0 * 5.0 * 3.14 Assert.AreEqual(setArea, targetObj.CalculateArea(),"面積計算") Assert.AreEqual(setArea * 10.0, targetObj.CalculateWeight (),"重さ計算") End Sub <Test()> _ Public Sub AnyBlock() Dim setArea As Double targetObj.Add(New Circle(10.0)) setArea = 5.0 * 5.0 * 3.14 targetObj.Add(New Snowman(20.0)) setArea += 4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14 targetObj.Add(New Rectangule(5.0, 10.0)) setArea += 5.0 * 10.0 Assert.AreEqual(setArea, targetObj.CalculateArea(),"面積計算") Assert.AreEqual(setArea * 10.0, targetObj.CalculateWeight (),"重さ計算") End Sub End Class End Namespace 25 Copyright© 2003 OSK Co., LTD. ③ 配列クラスを利用する .NET Framework には、複数のインスタンスを扱うためのコレクションクラスが用意されています。以下のクラス が代表的なクラスです。 ・ ArrayList サイズを動的に変更できる配列クラスです。 ・ Queue 先入れ先出しのコレクションクラスです。 ・ Stack 後入れ先出しのコレクションクラスです。 ・ Hashtable キーのハッシュコードによって編成されたキーと値を組み合わせたコレクションです。 ・ SortedList キーによって並べ替えられた、キーと値を組み合わせたコレクションです。 今回、ArrayList を使用して、ブロックを管理します。 C#の場合 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: using System; using System.Collections; namespace OSK.BuildingBlock.Core { public class BlockSet : Block { private ArrayList blockSetObj; public BlockSet() { blockSetObj = new ArrayList(); } public void Add( Block newBlock ) { blockSetObj.Add(newBlock); } public override double CalculateArea() { double result = 0.0; Block blockObj; int i; for( i = 0; i < blockSetObj.Count; ++i ) { blockObj = (Block)blockSetObj[i]; result += blockObj.CalculateArea(); } return result; } } } VB .NET の場合 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: Namespace OSK.BuildingBlock.Core Public Class BlockSet Inherits Block Private blockSetObj As ArrayList Public Sub New() blockSetObj = New ArrayList() End Sub Public Sub Add(ByRef newBlock As Block) blockSetObj.Add(newBlock) End Sub 26 Copyright© 2003 OSK Co., LTD. 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: Public Overrides Function CalculateArea() As Double Dim result As Double Dim blockOoj As Block Dim i As Integer For i = 0 To (blockSetObj.Count - 1) blockOoj = blockSetObj.Item(i) result += blockOoj.CalculateArea() Next Return result End Function End Class End Namespace Points .NET Framework では、すべてのクラスが1つの親クラスである Object クラスを継承しています。.NET では、 Object クラスの継承の記述は省略することができます。当然、提供されているクラスライブラリのクラスもすべて、 Object クラスを継承しています。したがって、.NET で現れるすべてのインスタンスは、Object クラスのインスタン スとして扱うことができます。このことは、すべてのインスタンスをポルモーフィックに扱うことができることを意味 しています。この性質を利用して、コレクションクラスは実装されています。 ④ ブロックセットをブロックとして処理するテストを追加する BlockSet がブロックとして扱えることをテストで確認します。 C#の場合 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: [Test] public void BlockSetIsBlock() { BlockSet newBlockSet; double setArea; target.Add( new Circle(10.0) ); setArea = 5.0 * 5.0 * 3.14; target.Add( new Snowman(20.0) ); setArea += 4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14; target.Add( new Rectangule(5.0, 10.0) ); setArea += 5.0 * 10.0; newBlockSet = new BlockSet(); newBlockSet.Add(new Circle(10.0)); newBlockSet.Add(new Snowman(20.0)); newBlockSet.Add(new Rectangule(5.0, 10.0)); target.Add(newBlockSet); setArea = setArea * 2.0; Assert.AreEqual(setArea, target.CalculateArea(), "面積計算"); Assert.AreEqual(setArea * 10.0, target.CalculateWeight(), "重さ計算"); } VB .NET の場合 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: <Test()> _ Public Sub BlockSetIsBlock() Dim newBlockSet As BlockSet Dim setArea As Double targetObj.Add(New Circle(10.0)) setArea = 5.0 * 5.0 * 3.14 targetObj.Add(New Snowman(20.0)) setArea += 4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14 targetObj.Add(New Rectangule(5.0, 10.0)) setArea += 5.0 * 10.0 27 Copyright© 2003 OSK Co., LTD. 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: newBlockSet = New BlockSet() newBlockSet.Add(New Circle(10.0)) newBlockSet.Add(New Snowman(20.0)) newBlockSet.Add(New Rectangule(5.0, 10.0)) targetObj.Add(newBlockSet) setArea = setArea * 2.0 Assert.AreEqual(setArea, targetObj.CalculateArea(), _ "面積計算") Assert.AreEqual(setArea * 10.0,targetObj.CalculateWeight (), _ "重さ計算") End Sub Step 2 インスタンスの作成を利用から切り離す ポルモーフィズムを利 用 するときの問 題 として、インスタンスの作 成 があります。これは、親 クラスだけですべての処 理 ができたとしても、インスタンスを作 成 には、具 体 的 な子 クラスが必 要 になるためです。ここまでの例 で言 い換 えれ ば、Block という抽 象 的 なクラスのインスタンスを作 ることはできなくて、できるのは具 体 的 な Circle クラスなどのブロッ クということです。この解 決 策 として、インスタンスを作 成 する場 所 を一 ヶ所 に限 定 する方 法 があります。 ① クラスを作成する ブロックを作成する BlockFactory クラスを作成します。このクラスは、作成できるブロックのリストを提供します。 また、指定されたリストのブロックを作ります。 C#の場合 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: using System; namespace OSK.BuildingBlock.Core { public class BlockFactory { private string[] BlockList; public BlockFactory() { BlockList = new string[5]; BlockList[0] = "直径 10 センチの丸形"; BlockList[1] = "高さ 20 センチの雪だるま"; BlockList[2] = "縦 5 センチ横 10 センチの四角形"; BlockList[3] = "5 センチの正方形"; BlockList[4] = "入門セット"; } public string[] GetBlockList() { return BlockList; } public Block CreateBlock( int blockIndex ) { return null; } } } 28 Copyright© 2003 OSK Co., LTD. VB .NET の場合 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: ② Namespace OSK.BuildingBlock.Core Public Class BlockFactory Private BlockList(4) As String Public Sub New() BlockList(0) = "直径 10 センチの丸形" BlockList(1) = "高さ 20 センチの雪だるま" BlockList(2) = "縦 5 センチ横 10 センチの四角形" BlockList(3) = "5 センチの正方形" BlockList(4) = "入門セット" End Sub Public Function GetBlockList() As String() Return BlockList End Function Public Function CreateBlock(ByVal blockIndex As Integer) As Block Return Nothing End Function End Class End Namespace テストクラスを作成する テスト用クラスとして、BlockFactoryTestCase を作成します。 ブロックの作成を依頼して、作成されたブロックのクラスのチェックと面積のテストを行います。 C#の場合 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class BlockFactoryTestCase { private BlockFactory target; [SetUp] public void Setup() { target = new BlockFactory(); } [TearDown] public void TearDown() { target = null; } [Test] public void CreateBlockTest() { Block newBlock; double AllArea; newBlock = target.CreateBlock(0); Assert.IsNotNull(newBlock,"ブロックが作成されない"); Assert.IsTrue(typeof(Circle) == newBlock.GetType(),"クラス違い"); Assert.AreEqual(5.0 * 5.0 * 3.14, newBlock.CalculateArea(),"面積計算" ); newBlock = target.CreateBlock(1); Assert.IsNotNull(newBlock,"ブロックが作成されない"); Assert.IsTrue(typeof(Snowman) == newBlock.GetType(),"クラス違い" ); Assert.AreEqual(4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14, newBlock.CalculateArea(),"面積計算" ); newBlock = target.CreateBlock(2); Assert.IsNotNull(newBlock, "ブロックが作成されない"); Assert.IsTrue(typeof(Rectangule) == newBlock.GetType() , "クラス違い"); Assert.AreEqual(5.0 * 10.0, newBlock.CalculateArea() , "面積計算"); newBlock = target.CreateBlock(3); Assert.IsNotNull(newBlock, "ブロックが作成されない"); Assert.IsTrue(typeof(Rectangule) == newBlock.GetType() , "クラス違い"); Assert.AreEqual(5.0 * 5.0, newBlock.CalculateArea(),"面積計算", ); 29 Copyright© 2003 OSK Co., LTD. 827: 828: 829: 830: 831: 832: 833: 834: 835: newBlock = target.CreateBlock(4); Assert.IsNotNull(newBlock,"ブロックが作成されない"); Assert.IsTrue( typeof(BlockSet) == newBlock.GetType() , "クラス違い"); AllArea = 5.0 * 5.0 * 3.14 + (4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14) + 5.0 * 10.0 + 5.0 * 5.0; Assert.AreEqual(AllArea, newBlock.CalculateArea() , "面積計算"); } } } VB .NET の場合 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: ③ Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class BlockFactoryTestCase Private targetObj As BlockFactory <SetUp()> _ Public Sub Setup() targetObj = New BlockFactory() End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub <Test()> _ Public Sub CreateBlockTest() Dim newBlock As Block Dim AllArea As Double newBlock = targetObj.CreateBlock(0) Assert.IsNotNull(newBlock, "ブロックが作成されない") Assert.IsTrue((TypeOf newBlock Is Circle), "クラス違い") Assert.AreEqual(5.0 * 5.0 * 3.14, newBlock.CalculateArea() _ "面積計算") newBlock = targetObj.CreateBlock(1) Assert.IsTrue((TypeOf newBlock Is Snowman), "クラス違い") Assert.AreEqual(4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14, newBlock.CalculateArea(), _ "面積計算") newBlock = targetObj.CreateBlock(2) Assert.IsTrue( (TypeOf newBlock Is Rectangule), "クラス違い") Assert.AreEqual(5.0 * 10.0, newBlock.CalculateArea() _ "面積計算") newBlock = targetObj.CreateBlock(3) Assert.IsTrue( (TypeOf newBlock Is Rectangule), "クラス違い") Assert.AreEqual(5.0 * 5.0, newBlock.CalculateArea()_ "面積計算") newBlock = targetObj.CreateBlock(4) Assert.IsTrue( (TypeOf newBlock Is BlockSet), "クラス違い") AllArea = 5.0 * 5.0 * 3.14 + (4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14) _ + 5.0 * 10.0 + 5.0 * 5.0 Assert.AreEqual(AllArea, newBlock.CalculateArea() _ "面積計算") End Sub End Class End Namespace クラスを完成させる C#の場合 880: 881: 882: 883: using System; namespace OSK.BuildingBlock.Core { 30 Copyright© 2003 OSK Co., LTD. 884: 885: 886: 887: 888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908: 909: 910: 911: 912: 913: 914: 915: 916: 917: 918: 919: 920: 921: 922: 923: 924: 925: 926: 927: 928: 929: 930: 931: 932: 933: public class BlockFactory { private string[] BlockList; public BlockFactory() { BlockList = new string[5]; BlockList[0] = "直径 10 センチの丸形"; BlockList[1] = "高さ 20 センチの雪だるま"; BlockList[2] = "縦 5 センチ横 10 センチの四角形"; BlockList[3] = "5 センチの正方形"; BlockList[4] = "入門セット"; } public string[] GetBlockList() { return BlockList; } public Block CreateBlock( int blockIndex ) { Block newBlock = null; switch( blockIndex ) { case 0: newBlock = new Circle(10.0); break; case 1: newBlock = new Snowman(20.0); break; case 2: newBlock = new Rectangule(5.0, 10.0); break; case 3: newBlock = new Rectangule(5.0, 5.0); break; case 4: BlockSet newBlockSet; newBlockSet = new BlockSet(); newBlockSet.Add( new Circle(10.0) ); newBlockSet.Add( new Snowman(20.0) ); newBlockSet.Add( new Rectangule(5.0,10.0) ); newBlockSet.Add( new Rectangule(5.0,5.0) ); newBlock = newBlockSet; break; default: break; } return newBlock; } } } VB .NET の場合 934: 935: 936: 937: 938: 939: 940: 941: 942: 943: 944: 945: 946: Namespace OSK.BuildingBlock.Core Public Class BlockFactory Private BlockList(4) As String Public Sub New() BlockList(0) = "直径 10 センチの丸形" BlockList(1) = "高さ 20 センチの雪だるま" BlockList(2) = "縦 5 センチ横 10 センチの四角形" BlockList(3) = "5 センチの正方形" BlockList(4) = "入門セット" End Sub Public Function GetBlockList() As String() Return BlockList End Function 31 Copyright© 2003 OSK Co., LTD. 947: 948: 949: 950: 951: 952: 953: 954: 955: 956: 957: 958: 959: 960: 961: 962: 963: 964: 965: 966: 967: 968: 969: 970: Public Function CreateBlock(ByVal blockIndex As Integer) As Block Dim newBlock As Block Select Case blockIndex Case 0 newBlock = New Circle(10.0) Case 1 newBlock = New Snowman(20.0) Case 2 newBlock = New Rectangule(5.0, 10.0) Case 3 newBlock = New Rectangule(5.0, 5.0) Case 4 Dim newBlockSet As BlockSet newBlockSet = New BlockSet() newBlockSet.Add(New Circle(10.0)) newBlockSet.Add(New Snowman(20.0)) newBlockSet.Add(New Rectangule(5.0, 10.0)) newBlockSet.Add(New Rectangule(5.0, 5.0)) newBlock = newBlockSet End Select Return newBlock End Function End Class End Namespace Step 3 インターフェイスを導入する 通 常 、クラスが他 のクラスを利 用 する場 合 、具 体 的 なクラスを参 照 します。しかし、利 用 される側 のクラスに変 更 が あった場 合 、必 ず利 用 する側 のクラスも再 コンパイルする必 要 があります。このように利 用 される側 と利 用 する側 の作 成 順 序 に制 約 が発 生 します。しかし、パソコンの基 盤 と CPU やハードディスクのように、インタフェースが標 準 的 に定 義 されていて、その定 義 を厳 守 していれば、利 用 する側 と利 用 される側 の作 成 される順 序 に制 約 がなくなります。ま た、利 用 される側 の機 能 アップしても、インターフェイスに変 更 がなければ、差 し替 えも可 能 です。インターフェイスは、 オブジェクト指 向 の概 念 のカプセル化 を最 も忠 実 に実 現 したものです。 VB.NET では、インターフェイスを定 義 することができるようになっています。このインターフェイスも、パソコンなどの 実 世 界 のインターフェイスと同 じで、定 義 だけで、実 装 は一 切 含 まれていません。インターフェイスには、メソッド、プ ロパティ、イベントを定 義 することができます。インターフェイスのイベント定 義 の書 式 は、以 下 のようになります。 C#の場合 public Interface インターフェイス名 { ‘メソッドやプロパティ、イベントの定義 } VB .NET の場合 Public Interface インターフェイス名 ‘メソッドやプロパティ、イベントの定義 End Interface 32 Copyright© 2003 OSK Co., LTD. ① インターフェイスを定義する インターフェイスは、クラスの特殊な形態ですから、クラスを作成する手順と同じです。一般的に、インターフェ イスは、先頭に I を付加します。 設定項目 カテゴリ テンプレート ファイル名 設定内容 コード(選択しなくても、特に問題はない) クラス IBlock.cs(VB の場合は IBlock.vb) インターフェイスは、型を定義するだけですので、すべてのメソッドやプロパティは必ず継承したクラスが実装す る必要があります。したがって、クラスには、public(VB の場合は Public)などの定義や abstract (VB の場合 は MustInherit)のキーワードは付加しません。ここで定義するインターフェイスは、プロパティとしてニックネ ームとメソッドとして面積の計算と重さの計算を用意しています。 C#の場合 971: 972: 973: 974: 975: 976: 977: 978: 979: 980: 981: 982: 983: 984: 985: using System; namespace OSK.BuildingBlock.Core { public interface IBlock { string Nickname { get; set; } double CalculateArea(); double CalculateWeight(); } } VB .NET の場合 986: 987: 988: 989: 990: 991: 992: ② Namespace OSK.BuildingBlock.Core Public Interface IBlock Property Nickname() As String Function CalculateArea() As Double Function CalculateWeight () As Double End Interface End Namespace インターフェイスを継承させる インターフェイスは、クラスと同様に継承させることができます。クラスの継承と異なり、インターフェイスは複 数のインターフェイスを継承させることができます。(クラスの継承は、一つだけです) インターフェイスの継承は、クラス継承と同じ「:」に続け、インターフェイス名を記述します。 (VB の場合はキー ワード Implements を使用します。)Block クラスに IBlock を継承させます。 インターフェイスのメソッドやプロパティを実装する場合も、クラスの実装であることを宣言します。 C#の場合 993: 994: 995: 996: 997: 998: using System; namespace OSK.BuildingBlock.Core { public abstract class Block : IBlock { 33 Copyright© 2003 OSK Co., LTD. 999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: private string nicknameValue; public string Nickname { get { return nicknameValue; } set { nicknameValue = value; } } public abstract double CalculateArea(); public double CalculateWeight() { return CalculateArea() * 10.0; } } } VB .NET の場合 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038: Namespace OSK.BuildingBlock.Core Public MustInherit Class Block Implements IBlock Private nicknameValue As String Public Property Nickname() As String _ Implements OSK.BuildingBlock.Core.IBlock.Nickname Get Return nicknameValue End Get Set(ByVal Value As String) nicknameValue = Value End Set End Property Public MustOverride Function CalculateArea() As Double _ Implements OSK.BuildingBlock.Core.IBlock.CalculateArea Public Function CalculateWeight () As Double _ Implements OSK.BuildingBlock.Core.IBlock.CalculateWeight Return CalculateArea() * 10.0 End Function End Class End Namespace Points インターフェイスのメソッドやプロパティを実装するときに個別に宣言を行うのは、複数のインターフェイスを実 装したときに、まったく名前や型が同じメソッドがあった場合でも、実装できるようにするためです。 ③ インターフェイスを利用する BlockFactory クラスの CreateBlock メソッドの返り値を IBlock に変更しましょう。BlockFactoryTestCase のテス トも変更します。 34 Copyright© 2003 OSK Co., LTD. Step 4 委譲を実装する 既 に定 義 されているクラス利 用 して、新 しいクラスを作 成 したいと考 えたときでも、必 ずしも継 承 ができるわけではあ りません。例 えば、利 用 できる機 能 が一 部 分 だけであったり、利 用 したいクラスが継 承 を禁 止 していたり、既 に他 のク ラスを継 承 する必 要 がある場 合 などです。このようなときは、継 承 ではなく、利 用 したいクラスを内 部 に持 つことで、解 決 することができます。このようなとき、外 部 からの処 理 依 頼 の一 部 をこの内 部 クラスに依 頼 することになります。この ようなクラスの利 用 方 法 を委 譲 と呼 びます。 ① クラスを作成する 四角形のクラス Rectangule を委譲で使用して、正方形クラスの Square クラスを新規作成します。(今回の場合、 継承を使用しても作成することができます) 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 Square.cs(VB の場合は Square.vb) クラスの初期のコードは以下のようになります。 C#の場合 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049: 1050: 1051: 1052: 1053: using System; namespace OSK.BuildingBlock.Core { public class Square : Block { public Square(double side) { } public override double CalculateArea() { return 0.0; } } } VB .NET の場合 1054: 1055: 1056: 1057: 1058: 1059: 1060: 1061: 1062: 1063: Namespace OSK.BuildingBlock.Core Public Class Square Inherits Block Public Sub New(ByVal side As Double) End Sub Public Overrides Function CalculateArea() As Double Return 0.0 End Function End Class End Namespace 35 Copyright© 2003 OSK Co., LTD. ② テストクラスを作成する Square クラスのテスト用クラスとして、SquareTestCase を作成します。 C#の場合 1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074: 1075: 1076: 1077: 1078: 1079: 1080: 1081: 1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091: 1092: 1093: 1094: 1095: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.Tests { [TestFixture] public class SquareTestCase { private Square target; [SetUp] public void Setup() { target = new Square(5.0); } [TearDown] public void TearDown() { target = null; } [Test] public void CalculateAreaTest() { Assert.AreEqual(5.0 * 5.0, target.CalculateArea(),"面積計算"); } [Test] public void CalculateWeightTest() { Assert.AreEqual(5.0 * 5.0 * 10.0, target.CalculateWeight(),"重さ計算"); } } } VB .NET の場合 1096: 1097: 1098: 1099: 1100: 1101: 1102: 1103: 1104: 1105: 1106: 1107: 1108: 1109: 1110: 1111: 1112: 1113: 1114: 1115: 1116: 1117: 1118: 1119: 1120: Imports NUnit.Framework Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class SquareTestCase Private targetObj As Square <SetUp()> _ Public Sub Setup() targetObj = New Square(5.0) End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub <Test()> _ Public Sub CalculateArea() Assert.AreEqual(5.0 * 5.0, targetObj.CalculateArea(), _ "面積計算") End Sub <Test()> _ Public Sub CalculateWeight () Assert.AreEqual(5.0 * 5.0 * 10.0, targetObj.CalculateWeight (),"重さ計算") End Sub End Class End Namespace 36 Copyright© 2003 OSK Co., LTD. ③ クラスを完成させる 処理はすべて、内部に持っている Rectangule のインスタンスに依頼します。 C#の場合 1121: 1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134: 1135: 1136: 1137: using System; namespace OSK.BuildingBlock.Core { public class Square : Block { Rectangule rectanguleObject; public Square(double side) { rectanguleObject = new Rectangule( side,side ); } public override double CalculateArea() { return rectanguleObject.CalculateArea(); } } } VB .NET の場合 1138: 1139: 1140: 1141: 1142: 1143: 1144: 1145: 1146: 1147: 1148: 1149: ④ Namespace OSK.BuildingBlock.Core Public Class Square Inherits Block Private rectanguleObject As Rectangule Public Sub New(ByVal side As Double) rectanguleObject = New Rectangule(side, side) End Sub Public Overrides Function CalculateArea() As Double Return rectanguleObject.CalculateArea() End Function End Class End Namespace クラスを利用する BlockFactory クラスの CreateBlock メソッド内の正方形を作るのに Rectangule を利用していた部分 Square に変 更します。 まず、はじめにテストを以下のように修正します。 C#の場合 1150: 1151: 1152: 1153: 1154: 1155: 1156: [TestFixture] public class BlockFactoryTestCase ここを Square になる { //中略 newBlock = target.CreateBlock(3); Assert.IsNotNull(newBlock, "ブロックが作成されない"); Assert.IsTrue(typeof(Square) == newBlock.GetType(),"クラス違い" ); VB .NET の場合 1157: 1158: 1159: 1160: 1161: 1162: 1163: <TestFixture()> _ Public Class BlockFactoryTestCase ‘中略 newBlock = targetObj.CreateBlock(3) Assert.IsTrue((TypeOf newBlock Is Square), "クラス違い") Assert.AreEqual(5.0 * 5.0, newBlock.CalculateArea(),_ "面積計算") テスト結果が緑になるようにクラスを変更する。 37 Copyright© 2003 OSK Co., LTD. Session3 Windows アプリケーションを作成する . Overview .NET を使 用 して Windows アプリケーションを作 成 します。.NET でも、VB6 と同 様 の手 順 で Widnows アプリケーシ ョンを作 成 することができます。しかし、Windows アプリケーションで問 題 となるのがテストです。ユーザインターフェイ スをテストする場 合 、手 作 業 で行 うことがほとんどでした。特 に、VB6 のプログラムでは、コントロールのイベントをハン ドルするメソッドの中 にすべての処 理 を記 述 することが多 かったため、自 動 がすることは非 常 に困 難 でした。テストツ ールを使 って、操 作 をトレースしても、結 果 は目 で確 認 しなければならなかったため、自 動 化 は行 われていないこと がほとんどでした。手 作 業 によるテストは、非 常 に時 間 が掛 かり、しかも退 屈 であるため、納 期 が近 づくと部 分 的 にし か行 われないことになりがちでした。結 果 として、リリース後 に不 具 合 が発 生 することも、まれではありませんでした。 .NETでも、特に意識しないでプログラムを作成するとまったく同じ状態に陥ることになります。これを回避する ために、必要になるのが、Form クラスに記述されていた処理を別のクラスに分離することです。また、この分離した クラスは、EXE を作成するプロジェクトではなく、クラスライブラリ(DLL 形式)のプロジェクトに作成する必要が あります。これは EXE を作成するプロジェクトのクラスは、外部から呼び出すことができないからです。呼び出せな いのでは、自動テストを行うことはできません。この Session では、.NET で強化された Windows アプリケーション に関する新機能を紹介するとともに、ユーザインターフェイスのテストについて説明を行います。 この Session でも、引き続き、積み木システムを例題として、作業を進めます。 Goal Windows アプリケーションを作成する ユーザインターフェイスをテストする Step 1 新規プロジェクトを作成する ① Windows アプリケーション用のプロジェクトを追加する 「ファイル」メニューの「プロジェクトの追加」から「新規プロジェクト」を選択して、新規プロジェクトの作成 を開始します。「各言語プロジェクト」の「Windows アプリケーション」テンプレートを使用して、次の設定でプ ロジェクトを作成します。 設定項目 プロジェクトの種類 テンプレート プロジェクト名 場所 ② 設定内容 各言語プロジェクト Windows アプリケーション GUI ¥dotNetSeminar¥Exercises¥ BuildingBlock¥Source プロジェクトのプロパティを設定する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで GUI プロジェクトのコンテキス トメニューを表示し、プロパティを選択します。以下の設定を変更します。 C#の場合 設定項目 アセンブリ名 既定の名前空間 設定内容 OSK.BuildingBlock.GUI OSK.BuildingBlock.GUI 38 Copyright© 2003 OSK Co., LTD. VB .NET の場合 プロパティ種別 全般 全般 ③ 設定項目 アセンブリ名 ルート名前空間 設定内容 OSK.BuildingBlock.GUI [空白] ユーザインターフェイス補助用のプロジェクトを追加する 「ファイル」メニューの「プロジェクトの追加」から「新規プロジェクト」を選択して、新規プロジェクトの作成 を開始します。 「各言語プロジェクト」の「クラスライブラリ」テンプレートを使用して、次の設定でプロジェクト を作成します。 設定項目 設定内容 プロジェクトの種類 各言語プロジェクト テンプレート クラスライブラリ プロジェクト名 UIState 場所 ¥dotNetSeminar¥Exercises¥ BuildingBlock¥Source Class1.cs(VB では.vb)は削除します。 ④ プロジェクトのプロパティを設定する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで UIState プロジェクトのコンテ キストメニューを表示し、プロパティを選択します。以下の設定を変更します。 C#の場合 設定項目 アセンブリ名 既定の名前空間 設定内容 OSK.BuildingBlock.UIState OSK.BuildingBlock.UIState VB .NET の場合 プロパティ種別 全般 全般 ⑤ 設定項目 アセンブリ名 ルート名前空間 設定内容 OSK.BuildingBlock.UIState [空白] プロジェクト間の参照設定を行う プロジェクト間の参照設定を行います。まず、 「GUI」プロジェクトで「Core」プロジェクトと「UIState」プロジ ェクトの参照設定を行います。次に、 「UIState」プロジェクトで「Core」プロジェクトの参照設定を行います。最 後に、 「Tests」プロジェクトに「UIState」プロジェクトの参照設定を行います。依存関係は、以下の図のようにな ります。 Core GUI UIState Tests 39 Copyright© 2003 OSK Co., LTD. Step 2 フォームを作成する ① フォームのコードを変更する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右 ボタンで Form1 のコンテキストメニューを表示し、 「コード表示」を選択 します。以下のようにコードを変更します。変更箇所は、名前空間とクラ ス名です。 C#の場合 1164: 1165: 1166: 1167: 1168: 1169: 1170: 1171: 1172: 1173: 1174: 1175: 1176: namespace OSK.OSK.BuildingBlock.GUI { public class BlockSuiteForm : System.Windows.Forms.Form // 中略 [STAThread] static void Main() { Application.Run(new BlockSuiteForm()); } } } VB .NET の場合 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: ② Namespace OSK.BuildingBlock.GUI Public Class BlockSuiteForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " #End Region End Class End Namespace ファイル名を変更する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで Form1 のコンテキストメニュー を表示し、「名前の変更」を選択します。ファイル名を BlockSuiteForm に変更します。 ③ プロジェクトの属性を変更する Form1 のクラス名を変更したので、関連する以下のプロジェクトの属性を設定します。 プロパティ種別 全般 設定項目 スタートアップの設定 設定内容 OSK.BuildingBlock.GUI.BlockSuiteForm 40 Copyright© 2003 OSK Co., LTD. ④ フォームにコントロールを配置する ツールボックスからコントロールをドラッグ・アンド・ドロップして、以下のようなウィンドウを作成します。 ⑤ コントロール名を変更する プログラムの作成をスムーズに進めるために、コントロール名を意味のあるものに変更します。また、各コントロ ールのプロパティを以下のように変更します。 WeightLabel BlockListBox WeightValueLabel SuiteMemberListBox AddButton DeleteButton BlockSuiteForm について、以下の設定を行う 種別 項目 表示 Text 配置 MinimumSize 設定内容 ブロックセット重量計算 400,350 WeightLabel について、以下の設定を行う 種別 項目 表示 Text 表示 Font 設定内容 重量トータル サイズを 18 41 Copyright© 2003 OSK Co., LTD. ⑥ WeightValueLabel について、以下の設定を行う 種別 項目 表示 Text 表示 Font 表示 BorderStyle 設定内容 [空白] サイズを 18 Fixed3D AddButton について、以下の設定を行う 種別 項目 表示 Text 設定内容 追加 DeleteButton について、以下の設定を行う 種別 項目 表示 Text 設定内容 削除 アンカーリングの設定を行う アンカーリングとは、ウィンドウのサイズを変更したときに、そのサイズに合わせて、コントロールの位置を自動 的に変更する機能です。 WeightLabel について 種別 位置 項目 Anchor 設定内容 Top,Left WeightValueLabel について 種別 項目 位置 Anchor 設定内容 Top,Left,Right AddButton について 種別 位置 設定内容 Top,Left 項目 Anchor DeleteButton について、以下の設定を行う 種別 項目 位置 Anchor 設定内容 Bottm,Left BlockListBox について 種別 位置 設定内容 Top, Bottom,Left 項目 Anchor SuiteMemberListBox について、以下の設定を行う 種別 項目 設定内容 位置 Anchor Top, Bottom, Left,Right ⑦ ウィンドウを表示させる プロジェクト GUI を一時的に「スタートアッププロジェクトに設定」を行い、ウィンドウを表示させてみる。 Topic VB.NET になって、フォームに必ず最前列に表示するためのプロパティ「TopMost」や半透明にするプロパティ 「Opacity」、更には、透過する色を設定するプロパティ「TransparencyKey」などが追加されている。 42 Copyright© 2003 OSK Co., LTD. Step 3 メニューを作成する ① フォームに、メニューコントロールをドロップする 「ツールボックス」ウィンドウから「MainMenu」コントロー ルをフォームに、ドラッグ&ドロップします。コントロール名 を「MainFormMenu」に変更します。 その後、ウィンドウ上のメニューに直接「終了(&X)」と入力し ます。 BlockSuiteForm のプロパティの Menu を MainFormMenu に 設定します。 ② メニュー「終了(&X)」のコードを作成する ウィンドウの「終了(&X)」メニューをダブルクリックすると、処理を行うコードが生成されます。そこに、以下の ように実装を行います。 C#の場合 1185: 1186: 1187: 1188: private void MenuItem1_Click(object sender, System.EventArgs e) { Application.Exit(); } VB .NET の場合 1189: 1190: 1191: 1192: 1193: Private Sub MenuItem1_Click _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MenuItem1.Click Application.Exit() End Sub Step 4 ユーザインターフェイスをテストする 今回のウィンドウでは、以下のようなクライアント部分のロジックを用意します。 ・ 初期表示のチェック ブロックのリストに正しく、ブロックが表示されている ・ 追加処理 指定されたブロックがリストに追加される ・ 削除処理 指定されたブロックがリストから削除される ・ 重量トータル表示 重量トータルは、カンマ編集されて少数点以下 1 桁で「xxxx.x グラム」と表示される 初期値、追加、削除によって、重量トータル表示が変更される ① クラスを作成する UIState プロジェクトに、フォーム BlockSuiteForm クラスの処理を制御するクラスとして BlockSuiteFormAction クラスを作成します。 設定項目 設定内容 カテゴリ コード(選択しなくても、特に問題はない) テンプレート クラス ファイル名 BlockSuiteFormAction.vs(VB の場合は vb) クラスを名前空間で囲み、利用するクラスライブラリ OSK.BuildingBlock.Core を定義します。 43 Copyright© 2003 OSK Co., LTD. 初期表示時にブロックのリストを表示するために必要なメソッド 2 つを用意します。 C#の場合 1194: 1195: 1196: 1197: 1198: 1199: 1200: 1201: 1202: 1203: 1204: 1205: 1206: 1207: 1208: 1209: 1210: using System; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.UIState { public class BlockSuiteFormAction { public int GetBlockListCount() { return 0; } public string GetBlockName( int index ) { return ""; } } } VB .NET の場合 1211: 1212: 1213: 1214: 1215: 1216: 1217: 1218: 1219: 1220: 1221: ② Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.UIState Public Class BlockSuiteFormAction Public Function GetBlockListCount() As Integer Return 0 End Function Public Function GetBlockName(ByVal index As Integer) As String Return "" End Function End Class End Namespace テストクラスを作成する BlockSuiteForm クラスをテストするクラスを BlockSuiteFormActionTestCase という名前で作成します。作成す るプロジェクトは、Tests プロジェクトです。 設定項目 カテゴリ テンプレート ファイル名 設定内容 コード(選択しなくても、特に問題はない) クラス BlockSuiteFormActionTestCase.cs(VB の場合は.vb) C#の場合 1222: 1223: 1224: 1225: 1226: 1227: 1228: 1229: 1230: 1231: 1232: 1233: 1234: 1235: 1236: 1237: using System; using NUnit.Framework; using OSK.BuildingBlock.Core; using OSK.BuildingBlock.UIState; namespace OSK.BuildingBlock.Tests { [TestFixture] public class BlockSuiteFormActionTestCase { private BlockSuiteFormAction target; [SetUp] public void Setup() { target = new BlockSuiteFormAction(); } 44 Copyright© 2003 OSK Co., LTD. 1238: 1239: 1240: 1241: 1242: 1243: 1244: [TearDown] public void TearDown() { target = null; } } } VB .NET の場合 1245: 1246: 1247: 1248: 1249: 1250: 1251: 1252: 1253: 1254: 1255: 1256: 1257: 1258: 1259: 1260: 1261: ③ Imports NUnit.Framework Imports OSK.BuildingBlock.Core Imports OSK.BuildingBlock.UIState Namespace OSK.BuildingBlock.Tests <TestFixture()> _ Public Class BlockSuiteFormActionTestCase Private targetObj As BlockSuiteFormAction <SetUp()> _ Public Sub Setup() targetObj = New BlockSuiteFormAction() End Sub <TearDown()> _ Public Sub TearDown() targetObj = Nothing End Sub End Class End Namespace 初期化テストを作成する 初期状態をチェックするテストを作成します。テストは、ブロックの数、そのブロックの名前をチェックします。 C#の場合 1262: 1263: 1264: 1265: 1266: 1267: 1268: 1269: 1270: 1271: [Test] public void InitTest() { Assert.AreEqual(5,target.GetBlockListCount(),"ブロックの数が合わない" ); Assert.AreEqual("直径 10 センチの丸形", target.GetBlockName(0) ); Assert.AreEqual("高さ 20 センチの雪だるま", target.GetBlockName(1) ); Assert.AreEqual("縦 5 センチ横 10 センチの四角形", target.GetBlockName(2) ); Assert.AreEqual("5 センチの正方形", target.GetBlockName(3) ); Assert.AreEqual("入門セット", target.GetBlockName(4) ); } VB .NET の場合 1272: 1273: 1274: 1275: 1276: 1277: 1278: 1279: 1280: 1281: 1282: 1283: 1284: 1285: 1286: <Test()> _ Public Sub InitTest() Assert.AreEqual(5, targetObj.GetBlockListCount (), _ "ブロックの数が合わない") Assert.AreEqual("直径 10 センチの丸形", _ targetObj.GetBlockName(0)) Assert.AreEqual("高さ 20 センチの雪だるま", _ targetObj.GetBlockName(1)) Assert.AreEqual("縦 5 センチ横 10 センチの四角形", _ targetObj.GetBlockName(2)) Assert.AreEqual("5 センチの正方形", _ targetObj.GetBlockName(3)) Assert.AreEqual("入門セット", _ targetObj.GetBlockName(4)) End Sub テストが通るように、クラスの実装を行います。 45 Copyright© 2003 OSK Co., LTD. ④ 追加と削除を実装する 追加処理と削除処理を実装します。BlockSuiteFormAction クラスを追加処理と削除処理を行うためにメソッド 4 つを用意します。 C#の場合 1287: 1288: 1289: 1290: 1291: 1292: 1293: 1294: 1295: 1296: 1297: 1298: 1299: 1300: public void AddBlock( int index ) { } public void DeleteBlock( int index ) { } public int GetSuiteMemberListCount() { return 0; } public double GetTotalWeight() { return 0.0; } VB .NET の場合 1301: 1302: 1303: 1304: 1305: 1306: 1307: 1308: 1309: 1310: Public Sub AddBlock(ByVal index As Integer) End Sub Public Sub DeleteBlock(ByVal index As Integer) End Sub Public Function GetSuiteMemberListCount() As Integer Return 0 End Function Public Function GetTotalWeight() As Double Return 0.0 End Function 追加処理と削除処理をチェックするテストを作成します。テストは、追加および削除処理を行い、ブロックの数、 合計の重さをチェックします。 C#の場合 1311: 1312: 1313: 1314: 1315: 1316: 1317: 1318: 1319: 1320: 1321: 1322: 1323: 1324: 1325: 1326: 1327: 1328: 1329: 1330: 1331: 1332: 1333: 1334: 1335: private const double Weight0 = 5.0 * 5.0 * 3.14 * 10.0; private const double Weight1 = (4.0 * 4.0 * 3.14 + 6.0 * 6.0 * 3.14) * 10.0; private const double Weight2 = 5.0 * 10.0 * 10.0; private const double Weight3 = 5.0 * 5.0 * 10.0; private const double Weight4 = Weight0 + Weight1 + Weight2 + Weight3; [Test] public void AddDeleteTest() { double totalWeight; Assert.AreEqual(0, target.GetSuiteMemberListCount(),"初期エラー"); Assert.AreEqual(0.0, target.GetTotalWeight(),"初期エラー"); target.AddBlock(4); totalWeight = Weight4; target.AddBlock(3); totalWeight += Weight3; target.AddBlock(0); totalWeight += Weight0; Assert.AreEqual(3, target.GetSuiteMemberListCount(),"追加エラー"); Assert.AreEqual(totalWeight, target.GetTotalWeight(),"追加エラー"); target.DeleteBlock(1); totalWeight -= Weight3; Assert.AreEqual(2, target.GetSuiteMemberListCount(),"削除エラー"); Assert.AreEqual(totalWeight, target.GetTotalWeight(),"削除エラー"); } 46 Copyright© 2003 OSK Co., LTD. VB .NET の場合 1336: 1337: 1338: 1339: 1340: 1341: 1342: 1343: 1344: 1345: 1346: 1347: 1348: 1349: 1350: 1351: 1352: 1353: ⑤ <Test()> _ Public Sub AddDeleteTest() Dim totalWeight As Double Assert.AreEqual(0, targetObj.GetSuiteMemberListCount(),"初期エラー") Assert.AreEqual(0.0, targetObj.GetTotalWeight(),"初期エラー") targetObj.AddBlock(4) totalWeight += Weight4 targetObj.AddBlock(3) totalWeight += Weight3 targetObj.AddBlock(0) totalWeight += Weight0 Assert.AreEqual(3, targetObj.GetSuiteMemberListCount(),"追加エラー") Assert.AreEqual(totalWeight, targetObj.GetTotalWeight(),"追加エラー") targetObj.DeleteBlock(1) totalWeight -= Weight3 Assert.AreEqual(2, targetObj.GetSuiteMemberListCount(),"削除エラー") Assert.AreEqual(totalWeight, targetObj.GetTotalWeight()",削除エラー") End Sub 重量トータルの文字列を実装する 重量トータルの文字列を実装します。BlockSuiteFormAction クラスを重量トータルの文字列を行うためにメソッド を用意します。 C#の場合 1354: 1355: 1356: 1357: public string GetTotalWeightString() { return ""; } VB .NET の場合 1358: 1359: 1360: Public Function GetTotalWeightString() As String Return "" End Function 重量トータルの文字列をチェックするテストを作成します。テストは、追加および削除処理のテストに追加を行い ます。 C#の場合 1361: 1362: 1363: 1364: 1365: Assert.AreEqual("0.0 グラム", target.GetTotalWeightString(),"初期エラー"); // 省略 Assert.AreEqual("4,202.8 グラム", target.GetTotalWeightString(),"追加エラー"); // 省略 Assert.AreEqual("3,952.8 グラム", target.GetTotalWeightString(),"削除エラー"); VB .NET の場合 1366: 1367: 1368: 1369: 1370: 1371: 1372: 1373: Assert.AreEqual("0.0 グラム", targetObj.GetTotalWeightString(), _ "初期エラー") ‘省略 Assert.AreEqual("4,202.8 グラム", targetObj.GetTotalWeightString(),_ "追加エラー") ‘省略 Assert.AreEqual("3,952.8 グラム", targetObj.GetTotalWeightString(), _ "削除エラー") また、3 桁の表示のカンマ編集については、以下のテストを行います。 C#の場合 47 Copyright© 2003 OSK Co., LTD. 1374: 1375: 1376: 1377: 1378: 1379: [Test] public void TotalWeightStringTest() { target.AddBlock(3); Assert.AreEqual("250.0 グラム", target.GetTotalWeightString(),"追加エラー"); } VB .NET の場合 1380: 1381: 1382: 1383: 1384: 1385: ⑥ <Test()> _ Public Sub TotalWeightStringTest() targetObj.AddBlock(3) Assert.AreEqual("250.0 グラム", targetObj.GetTotalWeightString(), _ "追加エラー",) End Sub 通知イベントを宣言する 重量トータルの変更をイベントとして実装します。 VB6 では、コマンドボタンなどのコントロールから通知されるイベントを使用したプログラムが行われまし た。.NET Framework が提供するイベントの機構は、デリゲートと呼ばれます。コントロールだけではなく、クラ スでもイベントを利用したプログラムを作成することができます。特徴として、非常に簡単に利用できることと、 イベントを発信するクラスは、イベントを受信する側のクラスに関する情報をまったく必要としないことです。 まず、はじめに、イベントをクラスの外で定義します。 C#の場合 1386: public delegate void WeightUpdateEventHandler(); VB .NET の場合 1387: Public Delegate Sub WeightUpdateEventHandler() 次に、発行するイベントをクラス内で宣言します。 C#の場合 1388: public event WeightUpdateEventHandler WeightUpdateEvent; VB .NET の場合 1389: Public Event WeightUpdateEvent As WeightUpdateEventHandler 最後に、初期化、追加、削除時にイベントを発行します。また、初期化メソッドも追加します。 C#の場合 1390: WeightUpdateEvent(); VB .NET の場合 1391: RaiseEvent WeightUpdateEvent() イベント発行のテストを行います。 C#の場合 初期化時に、イベントハンドラを追加します。ハンドラを new し、処理を行うメソッドと関連付けます。 1392: target.WeightUpdateEvent += new WeightUpdateEventHandler( WeightUpdateEventHandler ); 48 Copyright© 2003 OSK Co., LTD. VB .NET の場合 テストクラス内の BlockSuiteFormAction の宣言文に WithEvents を追加します。 1393: Private WithEvents targetObj As BlockSuiteFormAction 初期化処理を追加します。 C#の場合 1394: 1395: 1396: public void Initialize() { } VB .NET の場合 1397: 1398: Public Sub Initialize() End Sub 次に、テストの作成です。初期化および追加処理の前にフラグを False に設定し、イベントが発行されたら True に なるようにしてテストを行います。 イベント処理をするメソッドの定義は、VB6 のコントロールのイベント処理を追加する場合と同じ方法で作成する ことができます。具体的な操作は、エディタ上部の左のリストボックスから変数を選択して、右のリストボックス からイベントを選択します。 C#の場合 1399: 1400: 1401: 1402: 1403: 1404: 1405: 1406: 1407: 1408: 1409: 1410: 1411: 1412: 1413: 1414: 1415: 1416: 1417: private bool eventFlag; private void WeightUpdateEventHandler() { eventFlag = true; } [Test] public void InitializeWeightUpdateEventTest() { eventFlag = false; target.Initialize(); Assert.IsTrue(eventFlag,"Event 発行エラー"); } [Test] public void WeightUpdateEventTest() { eventFlag = false; target.AddBlock(3); Assert.IsTrue(eventFlag,"Event 発行エラー"); } VB .NET の場合 1418: 1419: 1420: 1421: 1422: 1423: 1424: 1425: 1426: 1427: 1428: 1429: 1430: 1431: 1432: 1433: Private eventFlag As Boolean Private Sub targetObj_WeightUpdateEvent() Handles targetObj.WeightUpdateEvent eventFlag = True End Sub <Test()> _ Public Sub InitializeWeightUpdateEventTest() eventFlag = False targetObj.Initialize() Assert.IsTrue(eventFlag,"Event 発行エラー") End Sub <Test()> _ Public Sub WeightUpdateEventTest() eventFlag = False targetObj.AddBlock(3) Assert.IsTrue(eventFlag,"Event 発行エラー") End Sub 49 Copyright© 2003 OSK Co., LTD. 最終的な BlockSuiteFormAction クラスは以下のようになります。 C#の場合 1434: 1435: 1436: 1437: 1438: 1439: 1440: 1441: 1442: 1443: 1444: 1445: 1446: 1447: 1448: 1449: 1450: 1451: 1452: 1453: 1454: 1455: 1456: 1457: 1458: 1459: 1460: 1461: 1462: 1463: 1464: 1465: 1466: 1467: 1468: 1469: 1470: 1471: 1472: 1473: 1474: 1475: 1476: 1477: 1478: 1479: 1480: 1481: 1482: 1483: 1484: 1485: 1486: 1487: 1488: 1489: 1490: 1491: 1492: 1493: 1494: 1495: 1496: 1497: 1498: 1499: using System; using System.Collections; using OSK.BuildingBlock.Core; namespace OSK.BuildingBlock.UIState { public delegate void WeightUpdateEventHandler(); public class BlockSuiteFormAction { private BlockFactory blockFactoryObject; private string[] blockListString; private ArrayList suiteMemberListObject; public event WeightUpdateEventHandler WeightUpdateEvent; public BlockSuiteFormAction() { blockFactoryObject = new BlockFactory(); suiteMemberListObject =new ArrayList(); blockListString = blockFactoryObject.GetBlockList(); } public void Initialize() { WeightUpdateEvent(); } public int GetBlockListCount() { return blockListString.Length; } public string GetBlockName( int index ) { return blockListString[index]; } public void AddBlock( int index ) { IBlock newBlock; newBlock = blockFactoryObject.CreateBlock(index); suiteMemberListObject.Add(newBlock); WeightUpdateEvent(); } public void DeleteBlock( int index ) { suiteMemberListObject.RemoveAt(index); WeightUpdateEvent(); } public int GetSuiteMemberListCount() { return suiteMemberListObject.Count; } public double GetTotalWeight() { int i; double totalWeight = 0.0; IBlock blockObject; for( i = 0; i < GetSuiteMemberListCount(); ++i ) { blockObject = (IBlock)suiteMemberListObject[i]; totalWeight += blockObject.CalculateWeight(); } return totalWeight; } public string GetTotalWeightString() { string totalWeightString; totalWeightString = string.Format("{0:#,##0.0}グラム", GetTotalWeight()); return totalWeightString; } } } 50 Copyright© 2003 OSK Co., LTD. VB .NET の場合 1500: 1501: 1502: 1503: 1504: 1505: 1506: 1507: 1508: 1509: 1510: 1511: 1512: 1513: 1514: 1515: 1516: 1517: 1518: 1519: 1520: 1521: 1522: 1523: 1524: 1525: 1526: 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538: 1539: 1540: 1541: 1542: 1543: 1544: 1545: 1546: 1547: 1548: 1549: 1550: 1551: Imports OSK.BuildingBlock.Core Namespace OSK.BuildingBlock.UIState Public Delegate Sub WeightUpdateEventHandler() Public Class BlockSuiteFormAction Public Event WeightUpdateEvent As WeightUpdateEventHandler Private blockFactoryObject As BlockFactory Private blockListString() As String Private suiteMemberListObject As ArrayList Public Sub New() blockFactoryObject = New BlockFactory() blockListString = blockFactoryObject.GetBlockList() suiteMemberListObject = New ArrayList() End Sub Public Sub Initialize() RaiseEvent WeightUpdateEvent() End Sub Public Function GetBlockListCount() As Integer Return blockListString.Length End Function Public Function GetBlockName(ByVal index As Integer) As String Return blockListString(index) End Function Public Sub AddBlock(ByVal index As Integer) Dim newBlock As IBlock newBlock = blockFactoryObject.CreateBlock(index) suiteMemberListObject.Add(newBlock) RaiseEvent WeightUpdateEvent() End Sub Public Sub DeleteBlock(ByVal index As Integer) suiteMemberListObject.RemoveAt(index) RaiseEvent WeightUpdateEvent() End Sub Public Function GetSuiteMemberListCount() As Integer Return suiteMemberListObject.Count End Function Public Function GetTotalWeight() As Double Dim i As Integer Dim totalWeight As Double Dim blockObject As IBlock For i = 0 To (GetSuiteMemberListCount() - 1) blockObject = suiteMemberListObject.Item(i) totalWeight += blockObject.CalculateWeight() Next Return totalWeight End Function Public Function GetTotalWeightString() As String Dim totalWeightString As String totalWeightString = String.Format("{0:#,##0.0}グラム", GetTotalWeight()) Return totalWeightString End Function End Class End Namespace 51 Copyright© 2003 OSK Co., LTD. Step 5 フォームウィンドウの処理を実装する ① フォームウィンドウにテスト済みにクラスを実装する BlockSuiteForm の コ ー ド を ひ ら き 、 利 用 す る ク ラ ス ラ イ ブ ラ リ の 宣 言 を 行 い ま す 。 次 に 、 BlockSuiteFormAction クラスのインスタンスをメンバ変数として宣言します。 C#の場合 初 期 化 時 に、イベントハンドラを追 加 します。 1552: 1553: 1554: 1555: 1556: 1557: 1558: 1559: 1560: 1561: 1562: 1563: 1564: 1565: 1566: 1567: 1568: 1569: 1570: 1571: 1572: 1573: 1574: 1575: 1576: 1577: using OSK.BuildingBlock.Core; using OSK.BuildingBlock.UIState; namespace OSK.OSK.BuildingBlock.GUI { public class BlockSuiteForm : System.Windows.Forms.Form { internal System.Windows.Forms.MainMenu MainFormMenu; internal System.Windows.Forms.MenuItem MenuItem1; internal System.Windows.Forms.Label SuiteMemberListLabel; internal System.Windows.Forms.Label BlockListLabel; internal System.Windows.Forms.Button DeleteButton; internal System.Windows.Forms.Button AddButton; internal System.Windows.Forms.ListBox SuiteMemberListBox; internal System.Windows.Forms.ListBox BlockListBox; internal System.Windows.Forms.Label WeightValueLabel; internal System.Windows.Forms.Label WeightLabel; private System.ComponentModel.Container components = null; private BlockSuiteFormAction actionObject; public BlockSuiteForm() ここがイベント追加 { // Windows フォーム デザイナ サポートに必要です。 InitializeComponent(); actionObject = new BlockSuiteFormAction(); actionObject.WeightUpdateEvent += new WeightUpdateEventHandler( WeightUpdateEventHandler ); } VB .NET の場合 このとき、WithEvents をつけてください。コンストラクタの中で、インスタンスの作成を行います。 1578: 1579: 1580: 1581: 1582: 1583: 1584: 1585: 1586: 1587: 1588: 1589: 1590: 1591: 1592: 1593: Imports OSK.BuildingBlock.Core Imports OSK.BuildingBlock.UIState Namespace OSK.BuildingBlock.GUI Public Class BlockSuiteForm Inherits System.Windows.Forms.Form Private WithEvents actionObject As BlockSuiteFormAction #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 actionObject = New BlockSuiteFormAction() End Sub 52 Copyright© 2003 OSK Co., LTD. ② フォームのロード時に初期化を行う フォームのロード処理で、コントロールの初期化と BlockSuiteFormAction の初期化を呼び出します。 このときに、BlockSuiteFormAction から作成できるブロック名を読み出して、リストボックスに追加 します。 C#の場合 1594: 1595: 1596: 1597: 1598: 1599: 1600: 1601: 1602: 1603: 1604: 1605: 1606: 1607: private void BlockSuiteForm_Load(object sender, System.EventArgs e) { InitializeBlockListBox(); actionObject.Initialize(); } private void InitializeBlockListBox() リストへ文字列を追加 { int i; for( i = 0; i < actionObject.GetBlockListCount(); ++i ) { BlockListBox.Items.Add(actionObject.GetBlockName(i)); } } VB .NET の場合 1608: 1609: 1610: 1611: 1612: 1613: 1614: 1615: 1616: 1617: 1618: 1619: ③ Private Sub BlockSuiteForm_Load _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load InitializeBlockListBox() actionObject.Initialize() End Sub Private Sub InitializeBlockListBox() Dim i As Integer For i = 0 To (actionObject.GetBlockListCount() - 1) BlockListBox.Items.Add(actionObject.GetBlockName(i)) Next End Sub 追加ボタンの処理を実装する 追加ボタンの処理として、もしリストボックスが選択されていたら、リストと BlockSuiteFormAction に選択さ れているブロックを追加する。 C#の場合 1620: 1621: 1622: 1623: 1624: 1625: 1626: 1627: 1628: 1629: 1630: 1631: 1632: 1633: 1634: 1635: 1636: 1637: 1638: 1639: 1640: private void AddButton_Click(object sender, System.EventArgs e) { if( IsBlockSelected() ) { int index; index = BlockListBox.SelectedIndex; actionObject.AddBlock(index); SuiteMemberListBox.Items.Add(actionObject.GetBlockName(index)); } } private bool IsBlockSelected() ブロックの選択状態を得る { if( BlockListBox.SelectedIndex >= 0 ) { return true; } else { return false; } } 53 Copyright© 2003 OSK Co., LTD. VB .NET の場合 1641: 1642: 1643: 1644: 1645: 1646: 1647: 1648: 1649: 1650: 1651: 1652: 1653: 1654: 1655: 1656: 1657: 1658: ④ Private Sub AddButton_Click _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles AddButton.Click If IsBlockSelected() Then Dim index As Integer index = BlockListBox.SelectedIndex actionObject.AddBlock(index) SuiteMemberListBox.Items.Add(actionObject.GetBlockName(index)) End If End Sub Private Function IsBlockSelected() As Boolean If BlockListBox.SelectedIndex >= 0 Then Return True Else Return False End If End Function 削除ボタンの処理を実装する 削除ボタンの処理として、もしリストボックスが選択されていたら、リストと BlockSuiteFormAction に選択さ れているブロックを削除する。 C#の場合 1659: 1660: 1661: 1662: 1663: 1664: 1665: 1666: 1667: 1668: 1669: 1670: 1671: 1672: 1673: 1674: 1675: 1676: 1677: 1678: 1679: private void DeleteButton_Click(object sender, System.EventArgs e) { if( IsSuiteMemberSelected() ) { int index; index = SuiteMemberListBox.SelectedIndex; actionObject.DeleteBlock(index); SuiteMemberListBox.Items.RemoveAt(index); } } private bool IsSuiteMemberSelected() { if( SuiteMemberListBox.SelectedIndex >= 0 ) { return true; } else { return false; } } VB .NET の場合 1680: 1681: 1682: 1683: 1684: 1685: 1686: 1687: 1688: 1689: Private Sub DeleteButton_Click _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles DeleteButton.Click If IsSuiteMemberSelected() Then Dim index As Integer index = SuiteMemberListBox.SelectedIndex actionObject.DeleteBlock(index) SuiteMemberListBox.Items.RemoveAt(index) End If End Sub 54 Copyright© 2003 OSK Co., LTD. 1690: 1691: 1692: 1693: 1694: 1695: 1696: ⑤ Private Function IsSuiteMemberSelected() As Boolean If SuiteMemberListBox.SelectedIndex >= 0 Then Return True Else Return False End If End Function 更新イベント処理を実装する C#の場合 ①で追加したイベントを処理するメソッドを実装します。 1697: 1698: 1699: 1700: private void WeightUpdateEventHandler() { WeightValueLabel.Text = actionObject.GetTotalWeightString(); } VB .NET の場合 エディタ上部の左のドロップダウン・リストボックスから actionObject を選択し、右のドロップダウン・リストボ ックスから、WeightUpdateEvent を選択します。イベントを処理するメソッドの雛型が作成されるので、処理を実 装します。 1701: 1702: 1703: 1704: ⑥ Private Sub actionObject_WeightUpdateEvent() _ Handles actionObject.WeightUpdateEvent WeightValueLabel.Text = actionObject.GetTotalWeightString() End Sub 実行する プロジェクト「GUI」をスタートアッププロジェクトに設定し、動作させます。最終的な動作を確認して、処理の 作成は終了です。 55 Copyright© 2003 OSK Co., LTD. Session4 Web アプリケーションを作成する Overview .NET を使用して Web アプリケーションを作成します。VS.NET では、Windows アプリケーションと同様の手順で、 Web アプリケーションを作成することができます。しかし、Web アプリケーションで問題となるのが状態の管理です。 多くのクライアントの処理を行わなければならない Web サーバ上でクライアントの状態を管理すると、リソースなど に問題となります。そこで、VS.NET では、.NET が提供するコントロールの状態を不可視コントロールに保持する機 構が組み込まれています。このことによって、フォームはそのつど作り直されますが、コントロールは状態を維持して いるように扱うことができます。しかし、作り直されることを意識しなくて良いわけではありません。 この Session でも、引き続き、積み木システムを例題として、作業を進めます。 Goal Web アプリケーションを作成する 状態管理を行う Step 1 仮想フォルダを作成する ① フォルダを作成する エクスプローラなどを使用して、「BuildingBlock」のディレクトリ配下の「Source」配下に、「WebUI」というデ ィレクトリを作成します。 ② IIS 上に、仮想フォルダを作成する コントロールパネルの「管理ツール」内の「イン ターネット インフォメーションサービス」を立 ち上げます。その後、コンテキストメニューから 「新規作成」の「仮想ディレクトリ」を選択して、 仮想フォルダを作成します。 仮想フォルダ名(エイリアス名)は、 「BuildingBlock」とします。また、ディレクトリ は、①で作成したフォルダを指定します。その他 の設定は、デフォルトのままです。 56 Copyright© 2003 OSK Co., LTD. Step 2 プロジェクトを作成する ① Web アプリケーション用のプロジェクトを追加する 「ファイル」メニューの「プロジェクトの追加」から「新規プロジェクト」を選択して、新規プロジェクトの作成 を開始します。 「各言語プロジェクト」の「Web アプリケーション」テンプレートを使用して、次の設定でプロジェ クトを作成します。 設定項目 プロジェクトの種類 テンプレート 場所 ② 設定内容 各言語プロジェクト ASP.NET Web アプリケーション http://localhost/BuildingBlock プロジェクトのプロパティを設定する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで BuildingBlock プロジェクトの コンテキストメニューを表示し、プロパティを選択します。以下の設定を変更します。 C#の場合 設定項目 アセンブリ名 既定の名前空間 設定内容 OSK.BuildingBlock.WebUI OSK.BuildingBlock.WebUI VB .NET の場合 プロパティ種別 全般 全般 設定項目 アセンブリ名 ルート名前空間 設定内容 OSK.BuildingBlock.WebUI [空白] ③ 57 Copyright© 2003 OSK Co., LTD. ④ Global.asax の名前空間を設定する Global.asax を選択して、コンテキストメニューから「コードの表示」を選択します。その後、以下のように名前空 間を設定します。 C#の場合 1705: 1706: 1707: 1708: 1709: 1710: 1711: 1712: namespace OSK.BuildingBlock.WebUI { public class Global : System.Web.HttpApplication { public Global() { InitializeComponent(); } VB .NET の場合 1713: 1714: 1715: 1716: 1717: 1718: Namespace OSK.BuildingBlock.WebUI Public Class Global Inherits System.Web.HttpApplication ‘省略 End Class End Namespace Topic 「Global.asax」とは、以下の処理をするための特別なページおよびモジュールです。 ① アプリケーション全体で使われるリソースの初期化および終了時のクリーンアップ処理 ② ユーザセッションが開始されたときの処理とセッションがクローズされたときの処理 ⑤ フォームのコードを変更する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで WebForm1 のコンテキストメニ ューを表示し、「コード表示」を選択します。以下のようにコードを変更します。 VB .NET の場合 1719: 1720: 1721: 1722: 1723: 1724: 1725: ⑥ Namespace OSK.BuildingBlock.WebUI Public Class BlockSuiteWebForm Inherits System.Web.UI.Page ‘省略 End Class End Namespace ファイル名を変更する 「ソリューション エクスプローラ」ウィンドウを表示して、マウスの右ボタンで WebForm1 のコンテキストメニ ューを表示し、「名前の変更」を選択します。ファイル名を BlockSuiteWebForm に変更します。 58 Copyright© 2003 OSK Co., LTD. ⑦ フォームにコントロールを配置する ツールボックスからコントロールをドラッグ・アンド・ドロップして、以下のようなフォームを作成します。 ⑧ コントロール名を変更する プログラムの作成をスムーズに進めるために、コントロール名を Windows アプリケーションのフォームと同じもの に変更します。また、各コントロールのプロパティを以下のように変更します。 DOCUMENT(BlockSuiteWebForm)について、以下の設定を行う 種別 項目 設定内容 表示 titile ブロックセット重量計算 WeightLabel について、以下の設定を行う 種別 項目 表示 Text 表示 Font 設定内容 重量トータル Large WeightValueLabel について、以下の設定を行う 種別 項目 表示 Text 表示 Font 表示 BorderStyle 設定内容 [空白] Large Double AddButton について、以下の設定を行う 種別 項目 表示 Text 設定内容 追加 DeleteButton について、以下の設定を行う 種別 項目 表示 Text 設定内容 削除 BlockListBox と SuiteMemberListBox は、名前以外は特に変更しない。 59 Copyright© 2003 OSK Co., LTD. Step 3 Web フォームの処理を実装する ① フォームウィンドウに入力補助クラスを実装する BlockSuiteWebForm のコードをひらき、利用するクラスライブラリの宣言を行います。次に、 BlockSuiteFormAction クラスのインスタンスをメンバ変数として宣言します。このとき、WithEvents をつけてください。コンストラクタの中で、インスタンスの作成を行います。 C#の場合 1726: 1727: 1728: 1729: 1730: 1731: 1732: 1733: 1734: 1735: 1736: 1737: 1738: 1739: 1740: 1741: 1742: 1743: using OSK.BuildingBlock.Core; using OSK.BuildingBlock.UIState; namespace OSK.BuildingBlock.WebUI { public class BlockSuiteWebForm : System.Web.UI.Page { protected System.Web.UI.WebControls.Label SuiteMemberListBoxLabel; protected System.Web.UI.WebControls.Label BlockListBoxLabel; protected System.Web.UI.WebControls.ListBox SuiteMemberListBox; protected System.Web.UI.WebControls.Button DeleteButton; protected System.Web.UI.WebControls.Button AddButton; protected System.Web.UI.WebControls.ListBox BlockListBox; protected System.Web.UI.WebControls.Label WeightValueLabel; protected System.Web.UI.HtmlControls.HtmlForm Form1; protected System.Web.UI.WebControls.Label WeightLabel; private BlockSuiteFormAction actionObject; VB .NET の場合 このとき、WithEvents をつけてください。コンストラクタの中で、インスタンスの作成を行います。 1744: 1745: 1746: 1747: 1748: 1749: 1750: 1751: 1752: 1753: 1754: 1755: 1756: 1757: 1758: 1759: 1760: ② Imports OSK.BuildingBlock.Core Imports OSK.BuildingBlock.UIState Namespace OSK.BuildingBlock.WebUI Public Class BlockSuiteWebForm Inherits System.Web.UI.Page Protected WithEvents WeightLabel As System.Web.UI.WebControls.Label Protected WithEvents WeightValueLabel As System.Web.UI.WebControls.Label Protected WithEvents BlockListBox As System.Web.UI.WebControls.ListBox Protected WithEvents AddButton As System.Web.UI.WebControls.Button Protected WithEvents DeleteButton As System.Web.UI.WebControls.Button Protected WithEvents SuiteMemberListBox As System.Web.UI.WebControls.ListBox Protected WithEvents BlockListBoxLabel As System.Web.UI.WebControls.Label Protected WithEvents SuiteMemberListBoxLabel As System.Web.UI.WebControls.Label Private WithEvents actionObject As BlockSuiteFormAction ページの初期化時に初期化を行う ページの初期化時に、コントロールの初期化と BlockSuiteFormAction の初期化を呼び出します。 このときに、BlockSuiteFormAction から作成できるブロック名を読み出して、リストボックスに追加 します。 C#の場合 イベント処理 1761: 1762: 1763: private void WeightUpdateEventHandler() { } 60 Copyright© 2003 OSK Co., LTD. 1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN: この呼び出しは、ASP.NET Web フォーム デザイナで必要です。 // InitializeComponent(); 初期化処理 base.OnInit(e); actionObject = new BlockSuiteFormAction(); actionObject.WeightUpdateEvent += new WeightUpdateEventHandler(WeightUpdateEventHandler); InitializeBlockListBox(); actionObject.Initialize(); } VB .NET の場合 1778: 1779: 1780: 1781: 1782: 1783: 1784: 1785: 1786: 1787: 1788: 1789: 1790: 1791: 1792: 1793: ③ Private Sub Page_Init _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Init ' CODEGEN: このメソッド呼び出しは Web フォーム デザイナで必要です。 ' コード エディタを使って変更しないでください。 InitializeComponent() actionObject = New BlockSuiteFormAction() InitializeBlockListBox() actionObject.Initialize() End Sub Private Sub InitializeBlockListBox() Dim i As Integer For i = 0 To (actionObject.GetBlockListCount() - 1) BlockListBox.Items.Add(actionObject.GetBlockName(i)) Next End Sub 追加ボタンの処理を実装する 追加ボタンの処理として、もしリストボックスが選択されていたら、リストと BlockSuiteFormAction に選択さ れているブロックを追加する。 C#の場合 1794: 1795: 1796: 1797: 1798: 1799: 1800: 1801: 1802: 1803: 1804: 1805: 1806: 1807: 1808: 1809: 1810: 1811: 1812: 1813: 1814: private void AddButton_Click(object sender, System.EventArgs e) { if( IsBlockSelected() ) { int index; index = BlockListBox.SelectedIndex; actionObject.AddBlock(index); SuiteMemberListBox.Items.Add(actionObject.GetBlockName(index)); } } private bool IsBlockSelected() { if( BlockListBox.SelectedIndex >= 0 ) { return true; } else { return false; } } 61 Copyright© 2003 OSK Co., LTD. VB .NET の場合 1815: 1816: 1817: 1818: 1819: 1820: 1821: 1822: 1823: 1824: 1825: 1826: 1827: 1828: 1829: 1830: 1831: 1832: ④ Private Sub AddButton_Click _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles AddButton.Click If IsBlockSelected() Then Dim index As Integer index = BlockListBox.SelectedIndex actionObject.AddBlock(index) SuiteMemberListBox.Items.Add(actionObject.GetBlockName(index)) End If End Sub Private Function IsBlockSelected() As Boolean If BlockListBox.SelectedIndex >= 0 Then Return True Else Return False End If End Function 削除ボタンの処理を実装する 削除ボタンの処理として、もしリストボックスが選択されていたら、リストと BlockSuiteFormAction に選択さ れているブロックを削除する。 C#の場合 1833: 1834: 1835: 1836: 1837: 1838: 1839: 1840: 1841: 1842: 1843: 1844: 1845: 1846: 1847: 1848: 1849: 1850: 1851: 1852: 1853: private void DeleteButton_Click(object sender, System.EventArgs e) { if( IsSuiteMemberSelected() ) { int index; index = SuiteMemberListBox.SelectedIndex; actionObject.DeleteBlock(index); SuiteMemberListBox.Items.RemoveAt(index); } } private bool IsSuiteMemberSelected() { if( SuiteMemberListBox.SelectedIndex >= 0 ) { return true; } else { return false; } } VB .NET の場合 1854: 1855: 1856: 1857: 1858: 1859: 1860: 1861: 1862: 1863: Private Sub DeleteButton_Click _ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles DeleteButton.Click If IsSuiteMemberSelected() Then Dim index As Integer index = SuiteMemberListBox.SelectedIndex actionObject.DeleteBlock(index) SuiteMemberListBox.Items.RemoveAt(index) End If End Sub 62 Copyright© 2003 OSK Co., LTD. 1864: 1865: 1866: 1867: 1868: 1869: 1870: ⑤ Private Function IsSuiteMemberSelected() As Boolean If SuiteMemberListBox.SelectedIndex >= 0 Then Return True Else Return False End If End Function 更新イベント処理を実装する エディタ上部の左のドロップダウン・リストボックスから actionObject を選択し、右のドロップダウン・リストボ ックスから、WeightUpdateEvent を選択します。イベントを処理するメソッドの雛型が作成されるので、処理を実 装します。 C#の場合 1871: 1872: 1873: 1874: private void WeightUpdateEventHandler() { WeightValueLabel.Text = actionObject.GetTotalWeightString(); } VB .NET の場合 1875: 1876: 1877: 1878: ⑥ Private Sub actionObject_WeightUpdateEvent() _ Handles actionObject.WeightUpdateEvent WeightValueLabel.Text = actionObject.GetTotalWeightString() End Sub 実行する プロジェクト「BuildingBlock」をスタ ートアッププロジェクトに設定します。 また、「BlockSuiteWebForm.aspx」を スタートページに設定します。実行を 行います。 一見問題なく動作しているようですが、 2 つの問題があります。 一つは、重量トータルの値が正しくな いことです。もう一つ、削除ボタンを 押すとエラーが発生することです。 なぜ、このようなエラーが発生するの で し ょ う か ? こ れ は 、 BlockSuiteFormAction のインスタ ンスが処理ごとに再作成されて、し かも状態が保持されないからです。 そこで、今回は、状態の保持されて いるセット内容のリストボックス から、BlockSuiteFormAction を再 構築することにします。 63 Copyright© 2003 OSK Co., LTD. Step 4 既存クラスに機能拡張する ① 必要とするメソッドを決定する リストボックスから BlockSuiteFormAction を再構築するためには、文字列によってセットリストにブロ ックを追加する必要があります。メソッドは、以下のようになります。 C#の場合 1879: 1880: 1881: public void AddBlock( string blockName) { } VB .NET の場合 1882: 1883: Public Sub AddBlock(ByVal blockName As String) End Sub Topic .NET では、引数が異なれば、同じ名前のメソッドを作成することができます。このような概念をオーバーロードと 呼びます。 例えば、VB6 では、 追加を行うメソッドを用意するときに、引数の型別に、AddInteger や AddLong、更には AddString などを用意する必要がありました。しかし、これでは、メソッド呼び出す側に負担をかけることになります。VB.NET では、Add という名前メソッドで、Overloads キーワード(省略可能です)を指定することで引数が異なったものを 複数用意することができます。また同様に VB6 では、メソッドに引数を追加して機能拡張する場合に、メソッド名に Ex を付加するように場合がありました。.NET では、引数を追加して、同じ名前で作成することができます。ここで は、AddBlock というメソッドを 2 つ作成しています。 ② テストメソッドを決定する 新規に追加する AddBlock をテストするためのメソッドを BlockSuiteFormActionTestCase クラスに追加します。 テストでは、ブロックの名前が必要になるため、定数として定義します。また、テストは、追加を行い、トータル の重さによって、結果の判別を行っています。 C#の場合 1884: 1885: 1886: 1887: 1888: 1889: 1890: 1891: 1892: 1893: 1894: 1895: 1896: 1897: [Test] public void AddNameTest() { double totalWeight = 0.0; target.AddBlock(BlockName4); totalWeight += Weight4; target.AddBlock(BlockName3); totalWeight += Weight3; target.AddBlock(BlockName0); totalWeight += Weight0; Assert.AreEqual(3, target.GetSuiteMemberListCount(),"追加エラー"); Assert.AreEqual(totalWeight, target.GetTotalWeight(),"追加エラー"); Assert.AreEqual("4,202.8 グラム", target.GetTotalWeightString(),"追加エラー"); } VB .NET の場合 1898: 1899: 1900: 1901: 1902: Private Const BlockName0 As String = "直径 10 センチの丸形" Private Const BlockName1 As String = "高さ 20 センチの雪だるま" Private Const BlockName2 As String = "縦 5 センチ横 10 センチの四角形" Private Const BlockName3 As String = "5 センチの正方形" Private Const BlockName4 As String = "入門セット" 64 Copyright© 2003 OSK Co., LTD. 1903: 1904: 1905: 1906: 1907: 1908: 1909: 1910: 1911: 1912: 1913: 1914: 1915: 1916: ③ <Test()> _ Public Sub AddNameTest() Dim totalWeight As Double targetObj.AddBlock(BlockName4) totalWeight += Weight4 targetObj.AddBlock(BlockName3) totalWeight += Weight3 targetObj.AddBlock(BlockName0) totalWeight += Weight0 Assert.AreEqual(3, targetObj.GetSuiteMemberListCount(),"追加エラー") Assert.AreEqual(totalWeight, targetObj.GetTotalWeight(),"追加エラー") Assert.AreEqual("4,202.8 グラム", targetObj.GetTotalWeightString(), _ "追加エラー") End Sub メソッドを実装する メソッド AddBlock は以下のようになります。 C#の場合 1917: 1918: 1919: 1920: 1921: 1922: 1923: 1924: 1925: 1926: 1927: 1928: 1929: 1930: 1931: 1932: 1933: 1934: 1935: 1936: 1937: public void AddBlock( string blockName) { int index; IBlock newBlock; index = GetIndex(blockName); newBlock = blockFactoryObject.CreateBlock(index); suiteMemberListObject.Add(newBlock); WeightUpdateEvent(); } private int GetIndex( string blockName ) { int i; for( i = 0; i < GetBlockListCount(); ++i ) { if( GetBlockName(i) == blockName ) { break; } } return i; } VB .NET の場合 1938: 1939: 1940: 1941: 1942: 1943: 1944: 1945: 1946: 1947: 1948: 1949: 1950: 1951: 1952: 1953: 1954: 1955: Public Sub AddBlock(ByVal blockName As String) Dim index As Integer Dim newBlock As IBlock index = GetIndex(blockName) newBlock = blockFactoryObject.CreateBlock(index) suiteMemberListObject.Add(newBlock) RaiseEvent WeightUpdateEvent() End Sub Private Function GetIndex(ByVal blockName As String) As Integer Dim i As Integer For i = 0 To (GetBlockListCount() - 1) If GetBlockName(i) = blockName Then Exit For End If Next Return i End Function 65 Copyright© 2003 OSK Co., LTD. Step 5 オブジェクトを再構築する ① ページのロード時に、オブジェクトを再構築する セット内容のリストボックスからブロック名を取得して、actionObject のブロックを追加するメソッドを呼び 出します。これで、状態の再構築は終了です。 C#の場合 1956: 1957: 1958: 1959: 1960: 1961: 1962: 1963: 1964: 1965: 1966: 1967: 1968: private void Page_Load(object sender, System.EventArgs e) { InitializeActionObject(); } private void InitializeActionObject() { int i; for( i = 0 ; i < SuiteMemberListBox.Items.Count; ++i ) { actionObject.AddBlock(SuiteMemberListBox.Items[i].ToString()); } } VB .NET の場合 1969: 1970: 1971: 1972: 1973: 1974: 1975: 1976: 1977: 1978: 1979: 1980: 1981: ② Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load ' ページを初期化する ユーザー コードをここに挿入します。 InitializeActionObject() End Sub Private Sub InitializeActionObject() Dim i As Integer For i = 0 To (SuiteMemberListBox.Items.Count - 1) actionObject.AddBlock(SuiteMemberListBox.Items(i).ToString()) Next End Sub 動作を確認する 実行を行って動作を確認します。 66 Copyright© 2003 OSK Co., LTD. 付録 1 NUnit リファレンス A) 属性 ③ TestFixture テスト用のクラスであることを宣言する。 ④ Test テストメソッドであることを宣言する。 ⑤ SetUp テストメソッド毎の初期化処理メソッドであることを宣言する。 ⑥ TearDown テストメソッド毎の終了処理メソッドであることを宣言する。 ⑦ Ignore(“理由”) 一時的にテストクラスやテストメソッドを無効にすることを宣言する。 引数に、理由を記述する ⑧ ExpectedException(type) テストメソッドから例外が発生されることを宣言する。発生しなかった場合はエラーとなる。 ⑨ TestFixtureSetUp テストクラスでクラス単位の初期化処理メソッドであることを宣言する。 ⑩ TestFixtureTearDown テストクラスでクラス単位の終了処理メソッドであることを宣言する。 B) Assert クラス ① IsTrue 条件が成り立ったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] condition 条件。 [入力] message 表示するメッセージ。 67 Copyright© 2003 OSK Co., LTD. ② IsFalse 条件が成り立たなかったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] condition 条件。 [入力] message 表示するメッセージ。 ③ IsNull オブジェクトがヌルだった、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] object 対象となるオブジェクト。 [入力] message 表示するメッセージ。 ④ IsNotNull オブジェクトがヌルでなかったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] object 対象となるオブジェクト。 [入力] message 表示するメッセージ。 ⑤ AreSame 比較するオブジェクトが同じインスタンスだったら、テストは成功とする。失敗した場合は、エラー結果を記録す る。 【引数】 [入力] object 比較対照となるオブジェクト。 [入力] object 比較対照となるオブジェクト。 [入力] message 表示するメッセージ。 68 Copyright© 2003 OSK Co., LTD. ⑥ AreEqual 予測値と実測値が同じだったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] expected 予測値。 [入力] actual 実測値。 [入力] message 表示するメッセージ。 ⑦ AreEqual 予測値と実測値の想定範囲内だったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] expected 予測値。 [入力] actual 実測値。 [入力] delta 範囲。 [入力] message 表示するメッセージ。 ⑧ Fail テストを失敗とする。 【引数】 [入力] message 表示するメッセージ。 69 Copyright© 2003 OSK Co., LTD. C) Assertion クラス ① Assert 条件が成り立ったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] condition 条件。 ② AssertEquals 予測値と実測値が同じだったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] expected 予測値。 [入力] actual 実測値。 ③ AssertEquals 予測値と実測値の想定範囲内だったら、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] expected 予測値。 [入力] actual 実測値。 [入力] delta 範囲。 70 Copyright© 2003 OSK Co., LTD. ④ AssertNotNull オブジェクトがヌルではない場合に、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] anObject オブジェクト。 ⑤ AssertNull オブジェクトがヌルの場合に、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] anObject オブジェクト。 ⑥ AssertSame オブジェクトが等しい場合に、テストは成功とする。失敗した場合は、エラー結果を記録する。 【引数】 [入力] message 表示するメッセージ。 [入力] expected 予測オブジェクト。 [入力] actual 実オブジェクト。 ⑦ Fail テスト失敗とする。 【引数】 [入力] message 表示するメッセージ。 71 Copyright© 2003 OSK Co., LTD.
© Copyright 2024 Paperzz