I’m having a problem designing part of my program (not writing it, for once!). This is kind of hard to explain without writing a novel, so I’ll try and be brief.
Basically, I have a program which reads/writes parameters from a piece of hardware. Currently, it does so over Serial, but eventually, I’ll like it to do so over USB, using the .NET wrapper for the FTDI chip http://www.ftdichip.com/Projects/CodeExamples/CSharp.htm
I think my problem is, I know I want several layers of abstraction, but I can’t seem to know where to draw the lines. First, I don’t want my ReadParam(), WriteParam(), and SendCommand() functions to be sitting in my main form class. That just seems cobbled. So obviously they should be in some other class, which I’ll instantiate. Let’s call that Comm for now.
The first option is, I could make an interface, lets say IComm, and have my Serial and USB flavors both implement that. The problem with this is, a large percentage of the code would be duplicated in both flavors, because I have special ReadReplyData() and other functions, that do pre-processing of the serial data before they return it to the GUI.
So the next option, is have Comm be an intermediary class, which defines an interface ICommDriver. Comm would implement a private ReadReplyData() formatting function, as well as the public ReadParam(), WriteParam(), and SendCommand() functions, while ICommDriver would specify only simpler Read and Write functions.
This all seems trivial except for two twists. One, I want this to be multi-theaded, obviously, so the GUI doesn’t hang. So I’m thinking that Comm would use a BackgroundWorker to do all the reads/writes. Also, the Serial flavor needs to be told which COM port to open (from a GUI drop-down), while the USB flavor does not. So do I make that part of the interface or not?
Thanks for your help everyone, I’ve been writing/deleting code for days trying to figure out the correct way to do this!
Jonathon
This is where your problem lies. It is a fundamentally flawed approach to development and it is exactly what leads to this paralysis. Develop several concrete implementations of the flavors first. Get them working in your application with kludgy
if type1 else type2logic. Then go back and refactor them all to share a common contract, common base, what have you. It will be blindingly obvious what needs to go where.With more details in the comments:
If you have shared code between implementations, you should use an abstract class. In my experience it’s best practice to keep the public methods final and call protected abstract methods from the public methods, like so:
Make each class self-contained. It should be single-instance, single-threaded and can be passed between workers and reporters.