Pluggable architecture
As I mention in my previous post I'll start to post on .Net implementation of architecture patterns. This time on Pluggable :
Name: Pluggable architecture.
Problem: need to add/remove classes/components from your running system dynamically without any changes to the running code.
Context: there are 3 conditions under which the pattern is applicable:
1) Handle in advance situation where one of the system service components known to be replaces in the system time scale. For example certain data provider known to be replacing on system time scale and this change shouldn’t affect existing code (data provider supply by system but eventually will be supply by other system).
2) System content in term of classes/components is unknown in advance. System content is determined dynamically in application boot start by outer configuration.
3) Certain actions should be taken in given situation as part of application flow (events/commands). There is: a) need to add new behavior as a result of new event or given command. B) minimize the code share by several programmers. For example code that use switch in order to handle deference commands arrived to the server. There is need to eliminate situation where several programmers update the switch code and enabling to act upon new commands.
Forces: Pluggable architecture is solving Modularity, independence, openness and composability issues in the system. Implementation of pluggable should take in account definition of: how and where to set pluggable settings. When, where and how to read pluggable settings. How to load dynamically components/classes. Which mechanism to use in order to load different classes without changing existing code.
Solution: Pluggable architecture is build from 6 parts:
1) Outer configurations file which connect friendly name to class and assembly. That assembly and class should be load by the system each time friendly name is given.
2) Parsing class to extract class and assembly by friendly name from configuration class.
3) Cache that should hold created classes for farther uses (for performance reasons).
4) Factory that receives friendly name and return class instance from cache or by creating new instance and store it in cache.
5) Interface that act as the contract between the called code and implement classes.
6) Classes that implement the contract interface and serve the given services eventually (in order to use this class, class name and assembly should be connect to friendly name inside configuration file).
To call pluggable class an interface type variable should be declared. Creation of variable instance must be just through the factory.
Static class diagram: this link.
Class description:
The solution is divided into 3 assemblies:
1) PluggableInterfaces
a. interface IPluggableDataAdapter: Holds the interface that should be implement by current and up to come pluggable classes.
2) CustomDataProvider
a. class DataProvider: sample data provider pluggable class that implement IPluggableDataAdapter.
3) PluggableDemo
a. class callingClass: simple class that demonstrate using pluggable classes.
b. class DataAdapterFactory : use to create and return instance of pluggable class. This class implements simple cache mechanism to hold pluggable classes instance as well. If pluggable class not cache already DataAdapterFactory uses PluggableNameExtractor to get pluggable class assembly and class names, create pluggable class instance and store it into cache.
c. class PluggableNameExtractor: extract pluggable class assembly name and full class name from configuration file.
You can add as much as friendly name as you want to config file thus enable to set which pluggable class will be called by given friendly name. This case matches the need to minimize shared code between programmers. Instead of long switch statement all you need is just several lines that get name of service and perform it.
Assembling application from different components is more complicated. The principle is the same but you need to create section in configuration file that holds given load classes. When system starts you can create instance for each one of those modules or use lazy load via factories to load assemblies just when they needed. This implementation is complicated due to the fact that all connections between system components should be based on contract interfaces.
You can plug in any class that implements IPluggableDataAdapter just by changing the assembly name and class name in the configuration file for given friendly name.
Resulting Context: to prevent performance impact cache of pluggable class should be use. In multi threaded environment consideration should be taking to prevent concurrency issues. In the demo I create new instance using reflection from cache instance. This is not recommend pattern I just use it for time constraint.
Examples: this link points to simple demo solution for pluggable architecture.