Efficiently centralise and manage Groups of Features (aka “lines of services”)

I mostly develop AddIns for Office. In the example of MS Word, I have an AddIn which allows to review drafting style, another AddIn which allows to monitor cross-references, another Addin which allows to import graphics from the web, etc. What the AddIns do is irrelevant here, but you should now they all work independently from each other.

I now would like to centralise all these AddIn 'Extensions' into one executable, so that the end users only deal with only one setup for Word, instead of 3. Due to some internal restriction rules, some users may not be allowed to use some AddIn extensions. Hence the need for some verifications at runTime.

At the moment I came up with the following arquitecture, but I am not 100% satisfied. I set below a listing of strenghts and weaknesses. Feel free to suggest aditionnal weaknesses, and/or fixes.

Strengths

1. The flow between the UI (button callbacks) and the execution is centralized and standardized.
2. The feature availability validation is centralised and easy to update if needs be.
3. Dealing with an new AddIn Extension is "easy": build a new class which inherits AddInExtension, populate the AddInExtensions Collection, populate the newly built Class with relevant callback names in the Overrides Sub FinallyExecute(), and add their corresponding features (Subs, Classes) into the same Class.
4. Encapsulation is total and there is no risk for interference between two extensions.

Weaknesses

1. I am worried I will have to include the entire code of an AddIn Extension into a single .vb file (the AddInExtensionABCServices Class).
2. I am also worried I will have to nest all AddIn Extension Sub-Classes into the Main-Class. I feel this can get messy.
3. Architecture is not 100% Hard Typed, in the sens that the callback Sub uses text strings twice to run the relevant Sub, whilst I would have loved to be able to do something like MyExtensions.ABCServices.GetChocolate.

Re point 7, my intuition tells me this could be fixed through (i) Shared Class or (ii) a combination of Module and Namespace. But (i) a MustInherit Class cannot be Shared, and (ii) Modules are hard to keep Private (default is Public, hence prone to errors).

Module Module1

Sub Main()

'... rest of initialization code

End Sub

'Example of a CallBack
Sub MyCallBackButtonClicked()
MyExtension.TryExecute("GetTea")
End Sub

End Module

'# Interface required for Intelisense when calling AddInExtensions As Collections.Hashtable()
Sub TryExecute(ByVal ActionName As String)
End Interface

'# Base Class from which will derive each AddIn Extension class

Protected Property Name As String
Protected Property StatusIsOk As Boolean

'This sub verifies the services are available
Public Sub TryExecute(ByVal ActionName As String) Implements IAddInExtension.TryExecute
If StatusIsOk Then
Me.FinallyExecute(ActionName)
Else
End If
End Sub

'The overriden version of this Sub will contain all the features related to a given Extension
Protected MustOverride Sub FinallyExecute(ByVal ActionName As String)

End Class

'# AddIn Extension Class for ONLINE SERVICES

Public Sub New()
MyBase.Name = "OnlineServices"
MyBase.StatusIsOk = True 'Let's say this AddIn Extension is PERMITED (typically would run a function here)
End Sub

'Actions available through the Button Callback for this AddIn Extension
Protected Overrides Sub FinallyExecute(ActionName As String)
Select Case ActionName
Case "GetTime"
Call Me.GetTime()
Case "GetWeather"
Call Me.GetWeather()
End Select
End Sub

Private Sub GetTime()
'...
End Sub

Private Sub GetWeather()
'...
End Sub

End Class

'# AddIn Extension Class for LOCAL SERVICES

Public Sub New()
MyBase.Name = "LocalServices"
MyBase.StatusIsOk = True 'Let's say this AddIn Extension is PERMITED (typically would run a function here)
End Sub

'Actions available through the Button Callback for this AddIn Extension
Protected Overrides Sub FinallyExecute(ActionName As String)
Select Case ActionName
Case "GetCoffee"
Call Me.GetCoffee()
Case "GetTea"
Call Me.GetTea()
End Select
End Sub

Private Sub GetCoffee()
'...
End Sub

Private Sub GetTea()
'...
End Sub

End Class

'# AddIn Extension Class for SPACE SERVICES

Public Sub New()
MyBase.Name = "SpaceServices"
MyBase.StatusIsOk = False 'Let's say this AddIn Extension is FORBIDDEN (typically would run a function here)
End Sub

'Actions available through the Button Callback for this AddIn Extension
Protected Overrides Sub FinallyExecute(ActionName As String)
Select Case ActionName
Case "GetOxygen"
Call Me.GetOxygen()
Case "GetSun"
Call Me.GetSun()
End Select
End Sub

Private Sub GetOxygen()
'...
End Sub

Private Sub GetSun()
'...
End Sub

End Class


Also one question aside: I would like to change

Dim MyExtension As IAddInExtension = AddInExtensions("LocalServices")
MyExtension.Execute("GetTea")


by

AddInExtensions("LocalServices").Execute("GetTea")


but Intelisense does not pick up the type so .Execute does not show in the dropwodn list. Other than minorly inconvenient, would that cause any problem?

I was thinking of typing the Hashtable with something in the like of Private AddInExtensions As New Collections.Hashtable(Of IAddInExtension) but the compiler does not like it.

Private AddInExtensions As New Generic.Dictionary(Of String, IAddInExtension)

AddInExtensions("LocalServices").TryExecute("GetTea")