I am a developer who works primarily with embedded devices (programmed with C and assembly). My C# and OOP knowledge is very limited (although I can fake my way through it when necessary). I have five devices that interface with a PC via USB开发者_开发百科. The PC does some calculations and sends a result to the device. The same calculations are performed for each device, but the calculations are done differently. For each device, I have a C# Windows Forms application that does some work and sends data back and forth to the device. Currently, I'm trying to get the five different applications merged into one so we can easily make changes, add new devices easily, and have a standard user interface. My problem is that I don't exactly know the best way to do it since I don't know which device will be used until run time. I'm trying to avoid a bunch of if statements and I would like to be able to put each device in a separate file. Here is some psudo-code of what I'm talking about.
class Device //This is what EVERY device can do
{
...
DoWork1();
DoWork2();
DoWork3();
...
}
class Device1
{
...
DoWork1(); //This is how the work is done for this device
DoWork2();
DoWork3();
...
}
class Device2
{
...
DoWork1(); //This is how the work is done for this device (not the same way as Device1)
DoWork2();
DoWork3();
}
public partial class frmMain : Form
{
private (some kind of object or something) CurrentDevice;
public frmMain()
{
...
//Determine what device (could be one of five) is currently being used
CurrentDevice = (which device is currently being used)
//Could be CurrentDevice = new Device1();
}
}
private void Button1_Click()
{
CurrentDevice.DoWork1(); //But this could be Device1.DoWork1() or Device2.DoWork1(), depending on what device is currently being used (which was determined in the frmMain constructor)
}
I'm not really sure, but I'm thinking I could use an interface or maybe inherit the Device1 class for the Device class and override the methods...But I don't know how I would have one generic way of saying CurrentDevice.DoWork1() since CurrentDevice could be Device1 or Device2.
Any ideas would be greatly appreciated. I'm using Visual Studio 2008 with .NET 3.5 on Windows XP SP3 and Windows 7.
I hope I described the problem well enough. If not, or if I didn't mention something that I should, please let me know. I'm new to stackoverflow and C#.
Thank you,
Michael
In your case, you're basically defining a inheritance hierarchy that can either consist of an abstract base class and two derived types or an interface with two implementors of it. For example
public abstract class BaseDevice
{
public abstract void DoWork1();
}
public class Device1 : BaseDevice
{
public override void DoWork1()
{
// provide implementation here
}
}
// also create Device2 : BaseDevice and implement
OR you could utilize an interface definition
public interface IDevice
{
void DoWork1();
}
public class Device1 : IDevice
{
public void DoWork1()
{
// provide implementation
}
}
Which methodology you pick is up to you. You would perhaps favor an abstract base class if, for example, you wanted to define some behavior or properties with implementations that were common throughout the hierarchy. With an abstract class, you can provide implementations. An interface is an empty contract, you cannot provide any common behaviors, only a definition for what behaviors or properties may be present.
Either way you go, you would refer to instances of the more derived type via the abstract or interface base. In this manner, you don't care what the implementing type is, only what it can do (it's methods or properties).
Example:
BaseDevice device1 = new Device1();
BaseDevice device2 = new Device2();
// maybe you have a list?
List<BaseDevice> devices = new List<BaseDevice> { device1, device2 };
foreach (BaseDevice device in devices)
{
device.DoWork1(); // notice you don't care about the actual type, just the behavior
}
I was a little confused at first because in this case the pc does calculations the devices only receive the result. So as I understand it you need different implementations of something on the PC, not the devices themselves.
The real trick here is not about using an interface or inheritance - you already figured that out. The trick is getting the right implementation type, and you use a factory for that part.
But you do have to decide on inheritance vs interface as well.
Use inheritance only if that "something" is truly part of a common, but also meaningful family. Inheritance should have a very strong "is a" element.
OTOH many objects could exist that might do a calculation but that you might not want to make a family. This is where composition is useful. To get that by inheritance you would need to have them share a common base class. Here you can use composition to allow each object to use a common interface to allow the pc to perform the calculation.
I suggest this approach.
You should have a reference to a common, generic interface, IDoCalculation, or some such, that defines a method signature that will be called in the same way for any device.
Next you have to get the device specific implementation for that interface, this is where each device can have a different implementation. Create a class for each device type/implementation.
Now the trick is to get the class you need without having to know what it is. To once again keep the details hidden and make the method calls generic, you can create a parameterized Factory. This factory accepts a parameter that describes what device the pc needs a calculation for. It then interprets that parameter and based on that creates a specific class that implements IDoCalculation. This is returned and you are done.
I leave it to you to figure out how these objects need to be organized into different assemblies...
//Common interface
public interface IDoCalculation
{
//Use whatever method signatures you need
int DoCalculation();
}
public class DeviceImplementation1 : IDoCalculation
{
#region IDoCalculation Members
public int DoCalculation()
{
//Device 1 Specific code goes here
}
#endregion
}
public class DeviceImplementation2 : IDoCalculation
{
#region IDoCalculation Members
public int DoCalculation()
{
//Device 2 Specific code goes here
}
#endregion
}
// A simple factory that does not require a lot of OOP understanding.
public class DeviceCalculationFactory
{
//Return a correct implementor based on the device type passed in
public IDoCalculation GetCalculationImplementationInstance(string devicetype)
{
switch (devicetype)
{
case "Device1":
return new DeviceImplementation1();
case "Device2":
return new DeviceImplementation2();
default:
//TODO ???
return null;
}
}
}
// A simple client that calls the methods and then send the results
public class DeviceManager
{
//To do the calculation, get an implementor for the correct device type from the factory - Presumably the PC knows the device of interest, example "Device1"
public void DoTheCalculationThing(string deviceType)
{
DeviceCalculationFactory factory = new DeviceCalculationFactory();
IDoCalculation calculation = factory.GetCalculationImplementationInstance(deviceType);
int result = calculation.DoCalculation();
// now send the result to the device
}
}
You might be interested in looking at some design patterns for this. http://www.oodesign.com/
Specifically Abstract Factory and Template Method. I think one of those might be what you're looking for.
http://www.oodesign.com/abstract-factory-pattern.html
http://www.oodesign.com/template-method-pattern.html
As I understand it, you want to be able to have a base class, then inherit the base class functions and define them in subclasses. One of those patterns would probably work for your scenario.
Anthony Pegram's answer is excellent but you may want to take it a step further. It is conceivable that although it appears that all your devices are performing the same tasks you may find that some do not, in fact, perform all the tasks and yet others perform even more.
In such cases you may be tempted to alter the interface to add another DoWork5
or DoWork6
method and simply raise NotImplemented
exceptions on types that do not have the particular behaviour.
This is troublesome for many reasons. I would suggest (should you find yourself in this position) to take a look at making your roles explicit. You do this by creating interfaces that represent a particular role (or set of behaviours --- ties in with the interface segregation principle).
So you could have IMediaPlayer
with Play
, Pause
, Rewind
and another IMediaRecorder
with a Record
method. In this way you implement the relevant roles on your concrete classes.
HTH
精彩评论