對於程式功能的啟用,我想多半一開始大家接觸到的寫法就是用個Flag去判斷功能是否啟用,如果啟用就做某些動作,這樣的程式多半會在主要的程式邏輯中加入If判斷,這樣的寫法雖然在Flag有設的狀態才會執行主要功能,但若Flag未設定,該If判斷多少也都會對其效能造成影響,而且會讓程式的邏輯與新功能的程式混在一起,變得不易理解。因此這邊記錄一下另一種寫法,純粹是個人的小小經驗,小小體會。

這邊簡單的來看個例子: Class Executor

Private _enableLogData As Boolean
Public Property EnableLogData() As Boolean
    Get
        Return _enableLogData
    End Get
    Set(ByVal value As Boolean)
        _enableLogData = value
    End Set
End Property

Event Executing As EventHandler
Event Executed As EventHandler

Protected Sub OnExecuting(ByVal e As EventArgs)
    RaiseEvent Executing(Me, e)
End Sub

Protected Sub OnExecuted(ByVal e As EventArgs)
    RaiseEvent Executed(Me, e)
End Sub

Sub GoExecute()
    For i As Integer = 1 To 100000000
        OnExecuting(New EventArgs)
        '運行動作,這邊為了看出差異,故不做動作
        If EnableLogData Then
                '紀錄動作,這邊為了看出差異,故不做動作
        End If
        OnExecuted(New EventArgs)
    Next
End Sub

End Class

這樣的程式若用下面程式去做測試

Sub Main()
    Dim executor As New Executor
    Dim sw As Stopwatch = Stopwatch.StartNew
    executor.GoExecute()
    Console.WriteLine(sw.ElapsedMilliseconds.ToString)

    executor.EnableLogData = True
    sw.Reset()
    sw.Start()
    executor.GoExecute()
    Console.WriteLine(sw.ElapsedMilliseconds.ToString)
End Sub

輸出的結果如下

以這個例子來看,若換個想法改用事件動態繫結的方式去做,當啟動功能時才把功能的動作繫上,當關閉功能時把功能的動作給卸下,功能的動作就可以跟主要的邏輯分開,少了不必要的If判斷,同一個事件也可以繫上不同功能的動作。

Class Executor

Private _enableLogData As Boolean
Public Property EnableLogData() As Boolean
    Get
        Return _enableLogData
    End Get
    Set(ByVal value As Boolean)
        If _enableLogData <> value Then
            OnEnableLogDataChanging(New EventArgs)
            _enableLogData = value
            OnEnableLogDataChanged(New EventArgs)
        End If
    End Set
End Property

Event EnableLogDataChanging As EventHandler
Event EnableLogDataChanged As EventHandler
Event Executing As EventHandler
Event Executed As EventHandler

Protected Sub OnEnableLogDataChanging(ByVal e As EventArgs)
    RaiseEvent EnableLogDataChanging(Me, e)
End Sub

Protected Sub OnEnableLogDataChanged(ByVal e As EventArgs)
    RaiseEvent EnableLogDataChanged(Me, e)
End Sub

Protected Sub OnExecuting(ByVal e As EventArgs)
    RaiseEvent Executing(Me, e)
End Sub

Protected Sub OnExecuted(ByVal e As EventArgs)
    RaiseEvent Executed(Me, e)
End Sub

Sub GoExecute()
    For i As Integer = 1 To 100000000
        OnExecuting(New EventArgs)
        '運行動作,這邊為了看出差異,故不做動作
        OnExecuted(New EventArgs)
    Next
End Sub

Private Sub LogData(ByVal sender As Object, ByVal e As System.EventArgs)
    '紀錄動作,這邊為了看出差異,故不做動作        
End Sub

Private Sub Executor_EnableLogDataChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.EnableLogDataChanged
    RemoveHandler Executed, AddressOf LogData
    If EnableLogData Then
        AddHandler Executed, AddressOf LogData
    End If
End Sub

End Class