For what it's worth, I helped implement a module for a similar Composite Application. So what follows is based on anecdotal experience,
To your first point, our application or "shell" needs an explicit enumeration of "available" methods. We can supply this any way we wish, either explicitly (via config) or implicitly (via reflection). As a preference, I favor the former as it is less "magical".
To your second point, in addition to an explicit enumeration our shell must maintain a map from choice to implementation. Again, this may be accomplished any number of ways, but typically our enumeration is a list of "Types", and when a type is selected we request an implementation of that type from a factory. The easiest way to implement this pattern is to leverage an Inversion of Control container, such as Castle Windsor, Unity, Ninject, etc. To be honest, I don't remember what we used internally.
For example, consider,
// a simple Plain Old C Object that describes business methods.
public class BusinessMethod
{
// user-friendly name
public string Name { get; set; }
// type that actually implements
public Type ImplementationType { get; set; }
}
// ... meanwhile, back on the ranch ...
public void OnBusinessMethodSelection ()
{
// 1. if selected
if (BusinessMethodList.SelectedItem != null)
{
// 2. retrieve selected item
BusinessMethod selected =
(BusinessMethod)(BusinessMethodList.SelectedItem);
// 3. request implementation of selected item from
// IoC container
object implementation =
_container.Resolve (selected.ImplementationType);
}
}
- To your third point, we need a way for disparate parts to communicate. Unfortunately we cannot rely on design-time methods (ie Command and Data bindings), so we must implement our own Event Aggregation service. Basically a "singleton" (as in single instance not static class) that knows about subscribers and publishers, and preferably an implementation that offers strong typing of arguments. Fortunately for us, many a greater man have gone before us, and we may benefit from their experience. Check out Kent Boogaart's Event Hub.
Here is an example of how we would use an Event Aggregator
// an example of a strongly typed subject. notice how subject
// defines content. semanticly, when someone creates and publishes
// an instance of this subject, they are requesting someone show
// an analysis view based on data content,
public class AnalysisSubject
{
// subject content, in this case a data result from
// a business method
public object Data { get; set; }
}
public class MainWindow : ISubscriber<AnalysisSubject> ...
{
// use whatever implementation of an IoC container we like
// here i assume we abstract from implementation and use a
// custom interface IContainer that exposes functionality
// that we need
private readonly IContainer _container = null;
public class MainWindow ()
{
// we're teh r00tz! we create an instance of IoC
// container for use throughout application
IContainer _container = new CustomContainer ();
// our container exposes both parameterized and
// type-parameterized resolve methods
IEventHub events = _container.Resolve<IEventHub> ();
events.Subscribe<AnalysisSubject> (this);
}
#region ISubscriber<AnalysisSubject>
// part of strongly typed subscriptions is that we
// may now handle strongly typed publications! yay!
public void Receive (AnalysisSubject subject)
{
// 1. request to display analysis of data
Type analysisType = subject.Data.GetType ();
// 2. get view control based on payload type
//
// NOTE: implicit usage below is not consistent
// with previous invocations, here we are submitting
// a type of something we already have, and actually
// want back something that knows how to handle it.
// most IoC containers can provide this functionality
// through "facilities" add ons that accept a
// parameter\discriminator like below, and produce
// something in return.
Control control = (Control)(_container.Resolve (analysisType));
// [alternatively] if the above is too "magical" where
// IAnalysisFactory is an interface we define for this
// express purpose
//IAnalysisFactory factory = _container.Resolve<IAnalysisFactory> ();
//Control control = factory.GetAnalysisControlFor (analysisType);
// 3. assign subject data to control
Control.DataContext = subject.Data;
// 4. display control
}
#endregion
}
And an example of publication
public class SomeBusinessView
{
private readonly IEventHub _events = null;
// we cannot function without an event aggregator of
// some kind, so we declare our dependency as a contructor
// dependency
public SomeBusinessView (IEventHub events)
{
_events = events;
}
public void DoMyThang ()
{
// 1. do some business
MyBusinessData data = SomeBusinessFunction ();
// 2. publish complete event
AnalysisSubject subject = new AnalysisSubject () { Data = data, };
_events.Publish (subject);
}
}