I'm using C#, WPF, Prism 6.3 and using Unity as an IoC container.
Here's one part I don't quite understand and could use some clarity.
Scenario: My application will talk to multiple physical lasers. I created a LaserModule with Views/Viewmodels, as well as ILaser interface, and 3+ brand-specific laser classes (LaserA/B/C). Since all the Lasers have the same functionality (Connect, GetReading, Disconnect) I thought it was a good idea to make the ILaser interface so they all must implement these methods, but in whatever way they need to.
Furthermore, the module will read from a laser.cfg file and in that file it will have the settings for each laser, including the "Class" of the laser which I will use to instantiate the proper laser class with.
The overall idea is that the LaserModule will create however many lasers are in the config file, of the appropriate type, and then register them in a unity container so they can be access throughout other parts of the application. In the other parts of my application, the goal is that the programmer shouldn't need to know anything about the particular laser or specific class, instead just work using the interface. The interfaces will all be stored in a common module, so that should be the only dependency.
This is where I'm at now... and it appears to be working because when the .Greet() methods are called at the end, I see the greeting that I gave when using Activator.CreateInstance.
Is this approach realistic or am I totally missing the point?
namespace LaserModule
{
interface ILaser
{
void Greet();
}
}
namespace LaserModule
{
class LaserA: ILaser
{
private string greeting = "blank";
public LaserA(string greet)
{
this.greeting = greet;
}
public void Greet()
{
MessageBox.Show("LaserA - " + greeting);
}
}
}
public void Initialize()
{
//Create Laser objects based on what brand of laser is supplied
String className = ReadLaserClassNameFunc1();
Type t = Type.GetType("LaserModule." + className, true);
object t2 = (Activator.CreateInstance(t,"laser 1 greeting"));
ILaser myLaser1 = (ILaser)t2;
//Create Laser objects based on what brand of laser is supplied
className = ReadLaserClassNameFunc2();
t = Type.GetType("LaserModule." + className, true);
t2 = (Activator.CreateInstance(t,"laser 2 greeting"));
ILaser myLaser2 = (ILaser)t2;
//Create Laser objects based on what brand of laser is supplied
className = ReadLaserClassNameFunc3();
t = Type.GetType("LaserModule." + className, true);
t2 = (Activator.CreateInstance(t,"laser 3 greeting"));
ILaser myLaser3 = (ILaser)t2;
_unityContainer.RegisterInstance("Laser1", myLaser1, new ContainerControlledLifetimeManager());
_unityContainer.RegisterInstance("Laser2", myLaser2, new ContainerControlledLifetimeManager());
_unityContainer.RegisterInstance("Laser3", myLaser3, new ContainerControlledLifetimeManager());
...
//The following lines would in reality be called from a different assembly.
ILaser theLaser1 = _unityContainer.Resolve<ILaser>("Laser1");
ILaser theLaser2 = _unityContainer.Resolve<ILaser>("Laser2");
ILaser theLaser3 = _unityContainer.Resolve<ILaser>("Laser3");
theLaser1.Greet();
theLaser2.Greet();
theLaser3.Greet();
}
Update: Added interfaces and services...
ILaserService.cs
using System.Collections.Generic;
namespace xxx.Infrastructure.Interfaces
{
public interface ILaserService
{
void CreateLasers();
List<ILaser> GetLasers();
}
}
LaserService.cs
using xxx.Infrastructure.Interfaces;
using System;
using System.Collections.Generic;
using System.Windows;
namespace LaserModule
{
public class LaserService : ILaserService
{
readonly List<ILaser> _lasers = new List<ILaser>();
public LaserService()
{
}
public void CreateLasers()
{
//Create Laser objects based on config file
string nameSpace= GetType().Namespace;
string className = "LaserA";
Type t = Type.GetType(nameSpace + "." + className, true);
object t2 = (Activator.CreateInstance(t, "laser 1 greeting"));
_lasers.Add((ILaser)t2);
className = "LaserB";
t = Type.GetType(nameSpace + "." + className, true);
t2 = (Activator.CreateInstance(t, "laser 2 greeting"));
_lasers.Add((ILaser)t2);
className = "LaserC";
t = Type.GetType(nameSpace + "." + className, true);
t2 = (Activator.CreateInstance(t, "laser 3 greeting"));
_lasers.Add((ILaser)t2);
}
public List<ILaser> GetLasers()
{
return _lasers;
}
}
}
LaserModule.cs
using Microsoft.Practices.Unity;
using Prism.Modularity;
using Prism.Regions;
using Prism.Unity;
using System.Windows;
using System;
using xxx.Infrastructure.Constants;
using xxx.Infrastructure.Interfaces;
using System.Collections.Generic;
namespace LaserModule
{
{
public class LaserModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _unityContainer;
public LaserModule(IRegionManager regionManager, IUnityContainer unityContainer)
{
_regionManager = regionManager;
_unityContainer = unityContainer;
}
public void Initialize()
{
//Create and register instance of laser service
_unityContainer.RegisterInstance(typeof(ILaserService), _unityContainer.Resolve<LaserService>(),new ContainerControlledLifetimeManager());
//Pull instance out of container
ILaserService myLaserService = _unityContainer.Resolve<ILaserService>();
myLaserService.CreateLasers();
List<ILaser> myLasers = myLaserService.GetLasers();
MessageBox.Show("LaserModule Ref Hash:" + myLasers.GetHashCode().ToString());
//Connect and display feedback
bool allOK = true;
foreach (ILaser _laser in myLasers)
{
if (_laser.Connect() == false)
{
allOK = false;
}
}
if (allOK == true)
{
_regionManager.RegisterViewWithRegion(RegionNames.RightSideRegion, typeof(Views.LaserModuleUI1));
_regionManager.RegisterViewWithRegion(RegionNames.RightSideRegion, typeof(Views.LaserModuleUI2));
}else
{
MessageBox.Show("1 or more lasers failed");
}
}
}
}
Bootstrapper.cs InitializeModules()
protected override void InitializeModules()
{
// Initialize Modules
base.InitializeModules();
ILaserService myLaserService = Container.Resolve<ILaserService>();
List<ILaser> myLasers = myLaserService.GetLasers();
MessageBox.Show("Bootstrap Ref Hash:" + myLasers.GetHashCode().ToString());
}
So only my ILaser and ILaserService are in my Infrastructure project, so the Shell and LaserModule depend on Infrastructure but not each other. I'm checking the Hashcode when I create the lasers in LaserModule as well as when they are pulled out of the container in the bootstrapper and they both match up.
The bootstrapper won't need the lasers (other modules will) but for proof of concept I think I'm doing things the right way. Any insight?
Why not just have an ILaserService that maintains the lasers in your app? Then you can resolve the ILaserSerrvice and then ask it for the laser you care about.
Example:
ILaser laser = _laserService.GetLaser(Lasers.LaserOne);