My application is used for communication with electronic device. It reads some identification data (like device firmware version), some configuration data, and periodically data from sensors. Program is divided into three layers:
-
Data layer – simple structures describing how data are stored in device memory.
-
Business layer – classes used for communication with device. They contains structures from data layer and using them to store data when reading from and writing to device.
-
Presentation layer – user interface (WinForms); using businnes layer classes.
Assume that:
Data structures looks like:
public struct Configuration
{
public int Option1;
public int Option2;
}
public struct Visualization
{
public int Temperature;
public int Pressure;
}
Business layer classes wraps these structures and contains logic for communication:
public abstract class BaseEntity<DataStructureType>
{
protected DataStructureType dataStructure;
public BaseEntity()
{
this.dataStructure = new DataStructureType();
}
/*
device communication logic
*/
public abstract bool GetAllData();
}
public class ConfigurationEntity : BaseEntity<Configuration>
{
public override bool GetAllData()
{
//getting configuration data from device
}
public int Option1
{
get { return this.dataStructure.Option1; }
set { this.dataStructure.Option1 = value; }
}
public int Option2;
{
get { return this.dataStructure.Option2 * 100; }
set { this.dataStructure.Option1 = value / 100; }
}
}
public class VisualizationEntity : BaseEntity<Visualization>
{
public override bool GetAllData()
{
//getting visualization data from device
}
public int Temperature
{
get { return this.dataStructure.temperature; }
}
public float Pressure;
{
get { return Conversions.IntToPressure(this.dataStructure.pressure); }
}
}
And Conversions class used to convert binary data:
public static class Conversions
{
public static float IntToPressure(int parameter)
{
return PressureAlgorithmA(parameter);
}
private static float PressureAlgorithmA(int parameter)
{
//some algorithm (called A) to convert binary pressure to pressure in bars
}
}
Problem is new firmware version, because application should handle both device versions(older and newer). New version has been updated with couple of new structures to read, new conversion algorithms, new configuration options. It looks like:
public struct Configuration
{
/*
old options are here
*/
//and updated goes here:
public int Option3;
public int Option4;
}
public class ConfigurationEntity : BaseEntity<Configuration>
{
/*
old code is here
*/
//and updated goes here
public int Option3
{
get { return this.dataStructure.Option3; }
set { this.dataStructure.Option3 = value; }
}
public int Option4;
{
get { return this.dataStructure.Option4 * 20; }
set { this.dataStructure.Option4 = value / 20; }
}
}
Also Conversions has beem changed:
public static class Conversions
{
public static float IntToPressure(int parameter)
{
return PressureAlgorithmB(parameter);
}
private static float PressureAlgorithmB(int parameter)
{
//some algorithm (called B) to convert binary pressure to pressure in bars
}
}
Now I have to check for device version every time I need to use new funcionality (when changing Option3 or Option4 from Configuration or when I need to use new data etities or which algorithm use when showing pressure to user).
My question is how can I do that with OOP practicies? What is correct way to do this in C#?
I was thinking about factory pattern, but what with new options in Configuration, or with new data entities?
If you need to maintain the same architecture for different versions of external component (device in your case) the plug-in based architecture can help you.
For example, you can:
Every
BusinessLayerclass should implementIPluginInterface(say), as this the layer where actuall communication with device should happen.There should be some
Communicatorclass, who reads device version information and picks from collection of availableBusinessLayers, that one which is aable to manage required version.Just to give you an idea.