現在ログインしていません。
新規アカウント作成
ログイン

業務ロジックなどでデリゲートを用いたソースコードの整理

たとえばの前提

複数の会社で使用されております、とある業務ロジック、たとえばリストを生成するプログラムがあったとします。

そこで用いられている計算式が、会社のパターンによってまったく違ったとします。

当初、会社ごとの計算式を呼び出すようにプログラムを組んでみました。

    Private Enum KaishaPattern
        kabuskigaishaAAA
        BBByuugengaisha
        CCCcompany
    End Enum

    Private Function getKeisanKekkaA(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return x + y + z
    End Function
    Private Function getKeisanKekkaB(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return -x - y - z
    End Function
    Private Function getKeisanKekkaC(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return x * y * z
    End Function

    Private Sub KeisanList(ByVal companyPattern As KaishaPattern)
        Dim paramA As Long = 0
        Dim paramB As Long = 0
        Dim paramC As Long = 0
        Dim paramD As Long = 0

        Select Case companyPattern
            Case KaishaPattern.kabuskigaishaAAA
                paramA = getKeisanKekkaA(0, 1, 2)
                paramB = getKeisanKekkaA(10, 18, 31)
                paramC = getKeisanKekkaA(2, 4, 8)
                paramD = getKeisanKekkaA(9999, 12, 88)
            Case KaishaPattern.BBByuugengaisha
                paramA = getKeisanKekkaB(0, 1, 2)
                paramB = getKeisanKekkaB(10, 18, 31)
                paramC = getKeisanKekkaB(2, 4, 8)
                paramD = getKeisanKekkaB(9999, 12, 88)
            Case KaishaPattern.CCCcompany
                paramA = getKeisanKekkaC(0, 1, 2)
                paramB = getKeisanKekkaC(10, 18, 31)
                paramC = getKeisanKekkaC(2, 4, 8)
                paramD = getKeisanKekkaC(9999, 12, 88)
        End Select

        'paramA~Dを用いてごにょごにょ集計するなりする。ここでの計算方法は共通。

    End Sub

■リスト1:当初の目論見

会社のパターンを引数に取りたくない場合

考え方にもよりますが、上記でのKeisanListサブプロシージャでのSelect Caseでの分岐は、外面が似ているだけに、どうにか一つにまとめられないものかと思い、以下のようにします。

    Private Enum KaishaPattern
        kabuskigaishaAAA
        BBByuugengaisha
        CCCcompany
    End Enum

    Private Function getKeisanKekka(ByVal companyPattern As KaishaPattern,
                                    ByVal x As Long,
                                    ByVal y As Long,
                                    ByVal z As LongAs Long
        Dim keisanKekka As Long = 0
        Select Case companyPattern
            Case KaishaPattern.kabuskigaishaAAA
                keisanKekka = x + y + z
            Case KaishaPattern.BBByuugengaisha
                keisanKekka = -x - y - z
            Case KaishaPattern.CCCcompany
                keisanKekka = x * y * z
        End Select
        Return keisanKekka
    End Function

    Private Sub KeisanList(ByVal companyPattern As KaishaPattern)
        Dim paramA As Long = getKeisanKekka(companyPattern, 0, 1, 2)
        Dim paramB As Long = getKeisanKekka(companyPattern, 10, 18, 31)
        Dim paramC As Long = getKeisanKekka(companyPattern, 2, 4, 8)
        Dim paramD As Long = getKeisanKekka(companyPattern, 9999, 12, 88)

        'paramA~Dを用いてごにょごにょ集計するなりする。ここでの計算方法は共通。

    End Sub
■リスト2:計算式の一本化

これによりすっきりとした見た目になりましたし、プログラムの複雑さもほどよく呼び出され側に吸収されています。

この程度のロジックであれば、実際これ以上手を入れることはないと思われます。

デリゲートを用いた整理

しかし、getKeisanKekkaという関数に計算パラメータ以外の本質的でない値を混ぜたくないという要求であったり、計算が違うものを一つの関数にまとめるのが嫌だという要求があったり、より複雑なパターンであったりする場合など、上記では満足できない場合があります。

そういうときに使えるのが、デリゲートです。

実際にソースコードを見てみましょう。

    Private Enum KaishaPattern
        kabuskigaishaAAA
        BBByuugengaisha
        CCCcompany
    End Enum

    Private Function getKeisanKekkaA(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return x + y + z
    End Function
    Private Function getKeisanKekkaB(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return -x - y - z
    End Function
    Private Function getKeisanKekkaC(ByVal x As Long,
                                     ByVal y As Long,
                                     ByVal z As LongAs Long
        Return x * y * z
    End Function
    Private Delegate Function GetKeisanKekkaDelegate(ByVal x As Long,
                                                     ByVal y As Long,
                                                     ByVal z As LongAs Long

    Private Sub KeisanList(ByVal companyPattern As KaishaPattern)
        Dim getKeisanKekkaDlg As GetKeisanKekkaDelegate = Nothing
        Select Case companyPattern
            Case KaishaPattern.kabuskigaishaAAA
                getKeisanKekkaDlg = AddressOf getKeisanKekkaA
            Case KaishaPattern.BBByuugengaisha
                getKeisanKekkaDlg = AddressOf getKeisanKekkaB
            Case KaishaPattern.CCCcompany
                getKeisanKekkaDlg = AddressOf getKeisanKekkaC
        End Select

        Dim paramA As Long = getKeisanKekkaDlg.Invoke(0, 1, 2)
        Dim paramB As Long = getKeisanKekkaDlg.Invoke(10, 18, 31)
        Dim paramC As Long = getKeisanKekkaDlg.Invoke(2, 4, 8)
        Dim paramD As Long = getKeisanKekkaDlg.Invoke(9999, 12, 88)

        'paramA~Dを用いてごにょごにょ集計するなりする。ここでの計算方法は共通。

    End Sub
■リスト3:デリゲートを用いた一本化

上記のソースにおいて、まずKeisanListサブプロシージャを見てみましょう。

getKeisanKekkaDlgという変数を宣言して、それを分岐でサブプロシージャのアドレスを代入しています。

そのあとgetKeisanKekkaDlg変数のInvokeメソッドに対して引数を書いたものを各々の変数に代入しています。

これにより、最初の例、二番目の例と同じ結果が得られるのです。

これはどういうことかというと、アドレスを代入したFunctionをgetKeisanKekkaDlgを介して呼び出しているのです。

この仕組みを、Delegate(デリゲート、委譲)と言います。アドレスを代入したFunction、場合によってはSubに処理をゆだねるから委譲と言うのです。

引数の中に計算の本質とは関係のないパラメータを増やしたくない、呼び出し側で分岐を把握したい、などの要望があった場合には、この方法が使えます。

同じメソッド名で中身の違うことをしたい場合はインタフェースや継承などの仕組みを使うという方法もあります。

ただ、分岐が一種類のメソッドに集約できる場合は、デリゲートで済ませることも可能なのです。