デリゲートの使いどころ ①(共通処理)
デリゲートは作成するアプリケーションの性質によっては全くお目にかからない(使う必要にならない)機能ですので、イマイチ使いどころが分からないかもしれません。そこで、デリゲートの恩恵が受けられる場面を3回に渡って説明しようと思います。
共通処理を動的に分ける
例えば以下の処理を考えます。ボタン特有の処理が共通処理の間に挟まれている状況です。
private void button1_Click(object sender, EventArgs e) { Console.WriteLine("共通処理"); Console.WriteLine("button1の特別な処理"); Console.WriteLine("共通処理"); } private void button2_Click(object sender, EventArgs e) { Console.WriteLine("共通処理"); Console.WriteLine("button2の特別な処理"); Console.WriteLine("共通処理"); }
なお、わかりやすいようにConsole.WriteLineを使用していますが、実際はもう少し長い処理を考えます。
このプログラムをリファクタリングしようと思った時、少しでもプログラミングをしたことがある方なら、処理を別メソッド(VB.NETで言うサブプロシージャ)化しちゃえばいいと言うでしょう。
つまり、以下のような処理に変更するのではないでしょうか。
private void button1_Click(object sender, EventArgs e) { KyotuSyori(1); } private void button2_Click(object sender, EventArgs e) { KyotuSyori(2); } private void KyotuSyori(int flg) { Console.WriteLine("共通処理"); if (flg == 1) { Console.WriteLine("button1の特別な処理"); } else { Console.WriteLine("button2の特別な処理"); } Console.WriteLine("共通処理"); }
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click KyotuSyori(1) End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click KyotuSyori(2) End Sub Private Sub KyotuSyori(ByVal flg As Integer) Console.WriteLine("共通処理") If flg = 1 Then Console.WriteLine("button1の特別な処理") Else Console.WriteLine("button2の特別な処理") End If Console.WriteLine("共通処理") End Sub
共通処理をまとめて、引数flgによって特別な処理を分岐する方法です。これで未来永劫完結する場合であれば問題ありませんが、button3が増えるとどうなるでしょうか。
当然flgがもう一つ増えますので、elseifかswitch(VB.NETのSelect Case)する必要があります。これでは手続き型と何ら変わりませんし、主要処理をさわる必要がありますので当然バグチェックはbutton1とbutton2もbutton3と同じようにする必要があります。つまり改廃作業にとてつもなく長い時間と労力が必要になります。
また、変更が発生しなくなったとしても、flg=1がbutton1という関連性をいつまで覚えておく必要がありますので、何か問題があってコードを読み返した時、非常に面倒なことになります。
さて、ここでデリゲートの登場です。もし、特別処理が以下の原則に一致するようであれば、デリゲートを使えば変更に強いコードにすることが可能です。
・引数はデリゲートするメソッドの引数(数、型両方)と同一でなければならない
・戻り値はデリゲートするメソッドの戻り値と同一でなければならない
private delegate void testDlgt(); private void button1_Click(object sender, EventArgs e) { testDlgt d = new testDlgt(button1_toku); KyotuSyori(d); } private void button2_Click(object sender, EventArgs e) { testDlgt d = new testDlgt(button2_toku); KyotuSyori(d); } private void KyotuSyori(testDlgt dlgt) { Console.WriteLine("共通処理"); dlgt(); Console.WriteLine("共通処理"); } private void button1_toku() { Console.WriteLine("button1の特別な処理"); } private void button2_toku() { Console.WriteLine("button2の特別な処理"); }
Private Delegate Sub testDlgt() Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim d As New testDlgt(AddressOf button1_toku) KyotuSyori(d) End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click Dim d As New testDlgt(AddressOf button2_toku) KyotuSyori(d) End Sub Private Sub KyotuSyori(ByVal dlgt As testDlgt) Console.WriteLine("共通処理") dlgt() Console.WriteLine("共通処理") End Sub Private Sub button1_toku() Console.WriteLine("button1の特別な処理") End Sub Private Sub button2_toku() Console.WriteLine("button2の特別な処理") End Sub
メソッドの数は増えましたが、デリゲートを導入しただけで以下のメリットが増えました。
・フラグ定数による条件分岐が無くなった
・フラグ定数の意味を覚える必要が無くなった
・button3などが増えた場合はメソッドを追加してデリゲートにするだけでよい
・button3などが増えた場合はbutton3のみのテストをすればよい