.NETプログラミングの勘所 - IEquatableジェネリックインターフェイスを実装しよう
.NETプログラミングの勘所 - IEnumerableを有効利用しよう
並列処理プログラミングという観点から見ても重要です。IEnumerableインタフェースを実装しておけば、並列処理プログラミングがやりやすくなります。例えば、System.Threading.Tasks.Parallelクラスには、タスクベースで並列処理をする手段が用意されています。
その他にも、IEnumerableインタフェースを実装するクラスが多いです。.NETプログラミングをする上で、IEnumerableインタフェースは避けて通れない存在です。
.NETプログラミングの勘所 - インタフェースを有効利用しよう
もちろん、歴史的な理由だけで使用を勧めているのではありません。インタフェースが役立つ理由が3点以上あります。.NETプログラミングをするならば、インタフェースはおさえるべきものなのです。これからその理由を解説します。
その一、実装継承階層からの独立。オブジェクト指向プログラミングのフレームワークは複雑で、実装の継承を多用して作られています。それ故に、実装の継承階層を注目しがちですが、.NET Frameworkは、インタフェースの継承も多用されています。その理由はいろいろありますが、そのうちの一つが継承階層からの独立です。実装の継承は型にはまれば強力ですが、現実の問題が型にはまると限りません。現実世界では、階層からはみ出た問題も解決せねばなりません。そこで、階層的につながっていないオブジェクトを表わす手段が必要となってきます。その役割を果たすのがインタフェースです。インタフェースを使用知れば、より再利用性の高いプログラミングができます。
その二、オブジェクトの特徴を示せる。オブジェクトには様々な特徴があります。しかし、それらオブジェクトに共通した特徴や特筆するべき特徴があります。それらオブジェクトの特徴を、プログラムで記述できれば、より高度なプログラミングができます。しかもその可能性は無限大です。インタフェースを実装したオブジェクトは、様々な局面で活躍します。
その三、便利なインタフェースが既に用意されている。先ほど述べたように、.NET Frameworkには便利なインタフェースが沢山用意されていますし、インタフェースを利用する強力なメソッドも多いです。その恩恵を受けるために、ちょっとした手間をかけて、自分が作るオブジェクトに既存のインタフェースを持たせるべきです。そうすれば、.NET Frameworkの強力さを得られます。
インタフェースをおさえて、快適な.NETプログラミングライフを楽しみましょう♪
WFは何故シングルスレッドモデルなのか?
WFのスレッドがシングルスレッドモデルである理由について知るには、WFの動作原理と目標を理解せねばなりません。おさらいします。WFの最大の特徴はブックマークです。WFは距離に関係なく、プログラムの中断と再開を行えます。この素敵なブックマークは、.NETランタイム上のWFランタイムが制御する事により実現しています。
ここまでの説明で、WFランタイムが鍵を握っている事が分かって頂けると思います。話しを元に戻します。話しの主役であるWFランタイムは、有限オートマトンであり、アクティビティをキューに格納して管理しています。シングルスレッドでなくてはならない理由がここにあります。ブックマークを有限オートマトンで実現するには、スレッドアジリティなのはもちろんのこと、プロセスアジリティでなくてはなりません。つまり、プログラムが特定のスレッドやプロセスに結び付いてはならないのです。
スレッドが保持する変数をコンパイラとOSから見ると、メモリに割り当てられたスタック空間のデータです。このデータをプログラムから操作するには、特別な方法や手順が必要です。ここでWFが宣言的である事を思い出して下さい。特別な方法をWF側から用意すると、宣言的なプログラムではなくなってしまいます。
なおかつ、距離に関係なくプログラムをブックマーク化するには、特定のプロセスと結びついてはなりません。何故ならば、リモートコンピュータのプログラムである事を意識すると、宣言的なプログラムでなくなるからです。いつどこでどのようにプログラムを実行するのかは、WFランタイムが管理する領域なのです。
そして、利用者に意識させず、有限オートマトンでキューを操作するのは、ほぼ不可能と言ってもよいでしょう。もし、WFで真の並列処理を可能にしたければ、「位置独立性」、「拡張性」、「信頼性」の三つの要件を、プログラマに意識させずに満たさなくてはなりません。しかしそれをするには、高度な人工知能が必要となりオーバーヘッドが高すぎます。それが嫌ならば、プログラマが意識し、全てのアクティビティを位置に関係なく、並列でも正常に動くようにプログラミングしなくてはなりません。ですがそんな事をするぐらいならば、有限オートマトンの動作を考える必要がない、普通の並列プログラミングをする方が簡単です。
纏めます。今のところ、WFの存在意義を保つには、シングルスレッドモデルでなくてはなりません。WF4には擬似的な並列処理をする方法が用意されていますが、WFランタイムが並列化しているように見せているだけです。これは悪い知らせの様に聞こえるでしょう。しかし、そうではありません。無闇にスレッドを使用すると大変な被害をもたらします。また、無闇に複雑化すると弊害の方が大きくなります。従って、WFがシングルモデルである事は利点が大きいと言えます。
WFに限らず、制限は不便さだけではなく利点も生み出します。適切な利益を生み出すために敢えて制限を施したWFの設計思想と仕組みは、我々に多くの事を教えてくれます。
Windows Workflow Foundation ( WF )は凄い
WFの特徴はなんといってもブックマークです。この世には長時間稼働するプログラムが存在します。そうしたプログラムを安定して稼働させるのは意外と難しいです。何故ならば、プロセスが終了する状況がいくらでもあるからです。全ての状況を想定してプログラムする事は不可能ではないものの、大変に骨が折れる作業です。WFにはブックマーク機能があるのでその心配は要りません。
WFにはもうひとつ重要な側面があります。それは、宣言型データフロープログラミングが可能となる事です。WFは制御フローを抽象化して扱えますし、「値が入力される」といった外部からの刺激に自動反応してプログラムを実行してくれます。こういった特徴は、一般的な開発は勿論のことドメイン特化開発で非常に有効です。
話しが少し脱線しますが、宣言型データフロープログラミングになじみがない人が多いと思いますので少し解説します。宣言型を簡単に言うと、C言語のように細かくコンピュータに対する命令を記述するのではなく、何をさせたいのかを記述する方式です。なじみ深い例をあげると、SQLとLINQがこれに該当します。これらの例を思い返せば、宣言的プログラミングの強力さが分かって頂けると思います。
データフロープログラミングは、宣言型よりも知られていないと思います。データフロープログラミングとは、データ駆動でプログラムが実行される形態のプログラムを扱います。例えば、非データフロープログラムでは、print( inputValue )などとすると、変数inputValueが計算されるまでそれ以降のプログラムが実行されません。一方、データフロープログラミングの場合、変数の計算が終わる前に次のプログラムを実行し、変数の計算が終わったら表示します。プログラミングの可能性を広げ生産性もあげる、これから重要となる技術だと言えます。
技術者として非常に興味深いのは、ブックマークと宣言型データフロープログラミングを可能とする技術基盤です。WFを技術的側面から捉えると、その正体は有限オートマトンを処理するエンジンです。WFはアクティビティオートマトンと呼ばれる、有限状態機械でアクティビティ(再開可能なプログラム)のライフサイクルを管理しています。アクティビティは、自動でパッシブ化(永続記憶に保存)され、必要な時に再アクティブ化(保存されたプログラムをメモリで再現)されます。
重要なのはパッシブ化です。プログラムがパッシブ化される事により、何時でも何処でもプログラムを再開できます。また、アイドル時にはメモリを消費しません。WFはスレッドアジリティであり、特定のスレッドに拘束されませんから、マルチコアCPUを有効利用できます。
他にも興味を引く要素がありますが、長くなるので今回は止めておきます。とにかく、WFは非常に興味深い技術です。WFを学習する事をお勧めします。
C#で関数型プログラミングを楽しむ♪
using System;
class Piyo
{
private string m_msg;
public string Message
{
get { return this.m_msg; }
}
public Piyo( string msg )
{
this.m_msg = msg;
}
public static Piyo AddPiyo( Piyo p )
{
return new Piyo( p.Message + "ピヨ♪" );
}
}
class PiyoPiyo : Piyo
{
public PiyoPiyo( string msg ) : base( msg ){ }
public static PiyoPiyo AddPiyo( PiyoPiyo pp )
{
return new PiyoPiyo(
Piyo.AddPiyo( pp ).Message + "ピヨ♪" );
}
}
class Program
{
static void Main( string[] args )
{
string str = "C";
Console.WriteLine(
Piyo.AddPiyo( new Piyo( str ) ).Message );
Console.WriteLine(
PiyoPiyo.AddPiyo( new PiyoPiyo( str ) ).Message );
}
}
関数型プログラミングは、関数による戻り値でプログラミングを行います。ですが、通常(命令型)のオブジェクト指向プログラミングを行うとメソッドは副作用を生みます。そこで、関数型プログラミングに近づけるに、オブジェクトのインスタンスを受け取る静的メソッドを定義し、受け取ったインスタンスに変更を加えずに、新しいインスタンス生成して返す必要があります。あと、プロパティは読み取り専用にした方がよいでしょう。「何故、F#を採らない!」この様にすれば、副作用がない関数型プログラミング風のプログラミングを実現できます。もちろん、.NETで関数型プログラミングをしたいのであればF#を使えばよいの言うまでもありません。
真面目な話を言うと、このテクニックは並行処理に向いています。並行処理の対象となるクラスは、この様に定義すればいいかもしれません。「何故、F#を採らない!」
.NETテストプログラミング入門7
前回例として提示したGUIプログラミングのサンプルをテストするには次のようにします。
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
class WpfTest
{
[STAThread]
static void Main()
{
//型を取得(※AssemblyQualifiedNameを使用する)
Type piyoType = Type.GetType( "PiyoWindow, WpfTest_Target, " +
"Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" );
//テストの準備
Window target = ( Window ) Activator.CreateInstance( piyoType );
StackPanel panel = ( StackPanel ) target.Content;
TextBox box = ( TextBox ) panel.Children[ 0 ];
Button btn = ( Button ) panel.Children[ 1 ];
Label label = ( Label ) panel.Children[ 2 ];
//値を設定してからクリックイベントを呼び出す
string inputValue = "テスト";
box.Text = inputValue;
MethodInfo method = typeof( Button ).GetMethod( "OnClick",
BindingFlags.NonPublic | BindingFlags.InvokeMethod |
BindingFlags.Instance );
method.Invoke( btn, null );
//値をチェック
string right = inputValue + "ピヨ♪";
bool result = right.Equals( label.Content );
Console.WriteLine(
result == true ? "テスト成功" : "テスト失敗" );
}
}
このコードを見れば、GUIのテストが意外と簡単である事が分かると思います。GUIのテストは通常のプログラミング以外のなにものでもありません。リフレクションとWPFを知っていれば自然に思いつく方法です。これはテスト対象が簡単である事も影響していますが、テスト対象がサンプルよりも複雑であっても、テストコードの行数が増えるだけで考えそのものは変わりません。GUIテストに於いて重要なポイントは3つしかありません。
1つめのポイントは、非公開のクラスを呼び出す方法です。大概のアプリケーションは、そのアプリケーションしか使わない独自ウィンドウを公開したくありません。公開していないクラスを呼び出す方法は、.NETやJavaの場合はリフレクションです。他の言語の場合、違う方法でハックすれば同様の事が出来ます。アクセス修飾子の問題はテストに於いてよくある事なので、プログラマは非公開のクラスのインスタンスを生成する方法を習得するべきだと言えます。
2つめのポイントは、イベントを発生させる方法です。GUIもただのプログラムです。人間がクリックせねば絶対に動かないわけではありません。それどころかコンピュータはOSの働きもあり、イベントを呼び出す相手が人間の操作なのかプログラムなのかを気にしません。どのようなGUIフレームワークにも、コードでイベントを呼び出す方法があるはずです。今回はリフレクションが簡単なのでそうしましたが、他のリフレクションがない言語でも同様の事が出来るでしょう。
3つめのポイントは、仕様をはっきりさせる事です。どのコントロールが、どのようにして値を付け取り、どのような値を出力するのかを明確にせねばなりません。仕様が明確に定められていないとテストが出来ません。これはあくまでも私個人の感覚ですが、現場ではこの点を見過ごして、「GUIテストは難しいから人がチェックする」と言っている人が多いと感じています。確かにGUIの美的感覚をプログラムをチェックできません。ですが、全てのGUIプログラムの動作はチェックできます。テストできる部分は絶対にするべきです。GUIプログラムはテスト出来ないからバグがあるという言い訳はクライアントに通用しません。
どうやら、GUIプログラミングはどう見えるのかばかりが意識され、プログラム的にどういう動きなのかを見過ごす傾向があるようです。該当しないチームも多々存在するでしょうが、そうでないプロジェクトチームはそもそもGUIプログラムのテストを実施しているはずです。
駆け足でテストプログラミングについて書きました。この連載でテストプログラミングが通常のプログラミングである事が分かったと思います。この連載がテストは難しいと忌避する考えを変え、信頼性が高いシステムがつくられる一助になれば幸いです。
.NETテストプログラミング入門6
一般的にテストで難しいとされているプログラムは並行処理とGUIです。並行処理については前回述べましたので、今度はGUIのテストについての解説をします。
先ずはテスト対象となるGUIのコードを見てみましょう。
using System;
using System.Windows;
using System.Windows.Controls;
static class Program
{
[STAThread]
static void Main()
{
SampleApplication app = new SampleApplication();
app.Run();
}
}
class SampleApplication : Application
{
public SampleApplication()
{
this.Startup += ( object sender, StartupEventArgs e ) =>
{
PiyoWindow win = new PiyoWindow();
win.Show();
};
}
}
//テストの対象となるクラス
class PiyoWindow : Window
{
private StackPanel panel;
private TextBox inputValueBox;
Button piyoAddButton;
private Label outPutValueBox;
public PiyoWindow()
{
//Windowの設定
this.Title = "テスト対象ピヨ♪";
this.MinHeight = 150;
this.MaxHeight = 150;
this.MinWidth = 300;
this.MaxWidth = 300;
this.WindowStartupLocation =
WindowStartupLocation.CenterScreen;
//各種コントロールの配置
panel = new StackPanel();
this.Content = panel;
inputValueBox = new TextBox();
inputValueBox.Text = "ここに文字を入力して下さい。";
panel.Children.Add( inputValueBox );
piyoAddButton = new Button();
piyoAddButton.Content = "出力ピヨ♪";
piyoAddButton.Click += ( object sender, RoutedEventArgs e ) =>
outPutValueBox.Content = inputValueBox.Text + "ピヨ♪";
panel.Children.Add( piyoAddButton );
outPutValueBox = new Label();
panel.Children.Add( outPutValueBox );
}
}
このサンプルコードは、ボタンをクリックすると一番上のテキストボックスに入力した文字列に「ピヨ♪」を付け、一番下に表示されるラベルに表示するという実にシンプルなWPFプログラムです。このサンプルは単純ですが、GUIテストの本質を知る材料になります。答えを見る前に自分でテストコードをコーディングしてみて下さい。続く...
.NETテストプログラミング入門5
先ずは解答となるテストコードを掲載し、そのテストコードを元に解説します。
using System;
using System.IO;
using System.Threading;
static class ConsoleTester
{
private static Semaphore lockObj = new Semaphore(1, 1);
public static bool ConsoleOutCheck(
string right, Action proc, bool yield )
{
//ロック開始
lockObj.WaitOne();
//出力先を変えてテストを行いやすくする
TextWriter tmp = Console.Out;
StringWriter writer = new StringWriter();
Console.SetOut( writer );
//テスト対象を実行
Thread.Sleep( 100 );
proc();
//出力設定を元に戻す
if ( yield == true ) Thread.Yield();
Console.SetOut( tmp );
//出力値をテスト
bool result = writer.ToString().Equals( right );
//ロック解除
lockObj.Release();
//テスト結果を返す
return result;
}
}
class Piyo
{
public void Greeting()
{
Console.WriteLine( "おはピヨ♪" );
}
}
class HelloWorld
{
public void FirstProgramming()
{
Console.WriteLine( "Hello World!" );
}
}
class Test
{
static void Main( string[] args )
{
//Piyoクラスのテストを実行
string piyoMessage = null;
Thread pt = new Thread( () => {
Piyo piyo = new Piyo();
string right = "おはピヨ♪" + Environment.NewLine;
bool result = ConsoleTester.ConsoleOutCheck(
right,
() => piyo.Greeting(),
false );
piyoMessage = result == true ?
"Piyoテスト成功" : "Piyoテスト失敗";
} );
//HelloWorldクラスのテストを実行
string helloMessage = null;
Thread ht = new Thread( () => {
HelloWorld hello = new HelloWorld();
string right = "Hello World!" + Environment.NewLine;
bool result = ConsoleTester.ConsoleOutCheck(
right,
() => hello.FirstProgramming(),
true );
helloMessage = result == true ?
"HelloWorldテスト成功" : "HelloWorldテスト失敗";
} );
//並行的にテスト
pt.Start();
ht.Start();
pt.Join();
ht.Join();
//テスト結果を表示
Console.WriteLine( piyoMessage );
Console.WriteLine( helloMessage );
}
}
以上のように前回のテストコードを少し改良するだけで、並行的にテストが出来るようになります。このテストコードはポイントが3つあります。1つめのポイントは、同期オブジェクトを使用し排他制御をしている事です。ConsoleクラスのOutプロパティのように静的プロパティを使用する場合、並行処理の結果を仕様通りにする為に排他制御をせねばなりません。複数のスレッドが同時に静的なプロパティの値を変更すると、静的な値に依存するクラスは整合性のある結果を出せません。詳しくは今後.NET並行処理プログラミングの記事を連載しますのでそちらの方を読んで下さい。
2つめのポイントは、テストを実行するクラスのメソッドを静的にする事です。テストコードもまた普通のプログラムです。他者が理解しやすいように考慮せねばなりません。このテストは静的な値に依存するので、分かりやすいように静的メソッドにせねばなりません。そうしないとこのテストコードを書いていない人は、オブジェクト指向プログラミングは通常インスタンスを使用するので、インスタンスな値に依存するテストだという前提を持ちます。従って、注意を促すために静的メソッドにする必要があります。
3つめのポイントは、テストを実行するクラスを静的にする事です。コンソールの入出力機能を使用するこのクラスは明らかに静的なものです。何故ならば、Consoleクラスの静的なOutプロパティが関係するからです。コンソールはそもそもグローバルかつ共有的なものです。それを他者に伝える為に静的なクラスでなければなりません。
2つめと3つめのポイントは、テストを並行処理しない場合でも同じ事が言えます。テストコードを漫然とコーディングしてはなりません。テストの性質に応じたテストクラスを設計&実装せねばなりません。何故ならば、テストコードは仕様書としての役割も果たすからです。テストの性質と異なるテストコードはプログラマに誤解を与えます。意外とテストコードを意識しない人が多々見受けられます。しかしながら、テストコードは仕様を示す大切な資産です。よく考えてテストコードをコーディングしましょう。