Testable design with COM objects
What is a good way to design for testing and extensibility when a component used to complete a task could either be a COM component or a .NET component? Does it make sense to wrap the COM component completely and extract an interface? Here is a simple, completely contrived, RCW interface on a COM component, where "abc" is the acronym for the component maker:
public interface IComRobot
{
void abcInitialize(object o);
void abcSet(string s, object o);
void abcBuild();
void abcExit();
}
To me, the fact that the provider of the component chose to prefix all methods with something indicating their company is somewhat irritating. The problem is, I want to define other Robot components that perform the same actions, but the underlying implementation is different. It would be completely confusing to Robot builders to have to implement "abcAnything".
How should I go about building a RobotFactory with a simple implementation that works like this?
public class RobotFactory
{
public static IRobot Create(int i)
{
// // problem because ComRobot implements IComRobot not IRobot
if (i == 0) return new ComRobot();
if (i == 1) return new AdvancedRobot();
return new SimpleRobot();
}
}
Should I bite the bullet and accept the abc prefix in my interface, thus confusing robot implementers? Should I force a dependency on the Robot consumer to know when they are using the COM robot? None of these seem ideal. I'm thinking about an additional level of abstraction (that can solve everything, right?). Something like so:
public interface IRobot : IDisposable
{
void Initialize(object o);
void Set(string s, object o);
void Build();
void Exit();
}
public class ComRobotWrapper: IRobot
{
private readonly IComRobot m_comRobot;
public ComRobotWrapper()
{
m_comRobot = ComRobotFactory.Create();
}
public void Initialize(object o)
{
m_comRobot.abcInitialize(o);
}
public void Set(string s, object o)
{
m_comRobot.abcSet(s, o);
}
public void Build()
{
m_comRobot.abcBuild();
}
public void Exit()
{
m_comRobot.abcExit();
}
public void Dispose()
{
//...RELEASE COM COMPONENT
}
}
public class ComRobotFactory
{
public static IComRobot Create()
{
return new ComRobot();
}
}
I would then alter and use the RobotFactory like so:
public class RobotFactory
{
public static IRobot Create(int i)
{
if (i == 0) return new ComRobotWrapper();
if (i == 1) return new AdvancedRobot();
return new SimpleRobot();
}
}
public class Tester
{
// local vars loaded somehow
开发者_Go百科 public void Test()
{
using (IRobot robot = RobotFactory.Create(0))
{
robot.Initialize(m_configuration);
robot.Set(m_model, m_spec);
robot.Build();
robot.Exit();
}
}
}
I'm interested in opinions on this approach. Do you recommend another approach? I really don't want to take on a DI framework, so that is out of scope. Are the pitfalls in testability? I appreciate you taking the time to consider this lengthy issue.
That looks spot on to me. You are creating an interface that is right for your domain / application, and implementing it in terms of a thrid party component.
精彩评论