tags:

views:

75

answers:

3

I am helping my friend develop a Unit Converter. He asked me if I could help split up the work. So I am making a .dll called Unit. Unit is supposed to handle the conversions. I was brainstorm on how to do this and I came up with an idea to put give each section an enum (like enum Angle[Degrees, Radians, Gradians], enum Area[Square meters, square miles, ...], enum Energy[Newtons, Pascals, Joules, ...], ...). Then a main abstract class Unit with all the Convert methods
NOTE:
I am NOT asking you to code the .dll, I just want your opinion on HOW to make the .dll.

+2  A: 

Something to watch for when you have a many-to-many relationship like this (every value wants to be able to be converted to every other kind of value in its class) is that you'll end up writing a ton of methods if you use the brute force method of writing a conversion function for each possibility.

This is OK if you need it to work as fast as possible and want to make sure your error is controlled, but you suffer for code maintainability.

Something I've done in the past for converting between many units was to establish a 'base' unit that everything could convert to and write functions to convert to/from that. This lets you define an interface something like:

interface IUnitConverter<T, U> {
 T ToBaseUnit();
 void FromBaseUnit(T BaseValue);
 U MyValue {get;}
}

And specialize that for certain types of conversion:

class RadiansConverter : IUnitConverter<DegreesConverter, float> {
 float radians = 0;

 DegreesConverter ToBaseUnit() {...}
 void FromBaseUnit(DegreesConverter BaseValue) {...}
 float MyValue {get {return radians;}}
}

Edit: Usage:

// assume we have a constructor that sets the private value
float degrees = new RadiansConverter(Math.PI).ToBaseUnit().MyValue;
float radians = new RadiansConverter().FromBaseUnit(degrees).MyValue;

This can be somewhat heavyweight and you have to watch for propagation of error hitting you for two conversions instead of one. It has come in handy when I needed to do complex GIS conversions that needed state stored as well, but it might not be what you're looking for exactly.

Ron Warholic
A: 

Why one .dll?

  • You have n sets of convertible units: Angle, Energy, Area, Temperature, whatever.
  • You have, within each set, x possible units: Degrees, Radians and Gradians within Angle.

Within that set of x units, you have (if I remember my maths right) x! possible conversions, which isn't an issue when x = 3, but is a bit harder for Area, where you can have square millimetres, centimetres, metres, inches, feet, yards, chains, furlongs or miles, as well as hectares and acres, and that's without a trip to wikipedia to look up anything more obscure. 11! is just under 40 million possible conversions.

I'd be tempted to do one .dll (or more specifically one class and maybe put all the classes in the same .dll) for each of the n sets of convertible units and within each .dll convert everything via one standard unit so that you get away with running one or two of 2(x - 1) conversion calcs per conversion, say converting all input in to square metres and then out again rather than coding up every possible combination. Twenty conversions sounds like a much quicker development task than forty million :-)

I'd definitely suggest enumerations rather than free string literals for the unit descriptions, as that will make the classes much easier for your friend to use, and neatly expose the options for the available conversions.

I can't think of a nice way to have all the classes implement the same interface because each conversion will take two parameters from the enumerated types (from and to) and that enum will be different for each class. Generics may offer a cleaner solution, but I can't see how just now - someone else has answered while I've been typing, but I've not read that answer yet.

Xav
+1  A: 

I would prefer a design like:

var measure = Angle.FromDegrees(10);
Console.Write(measure.ToRadians());

Simply from a coding usability perspective I don't see any comfort in adding enums.

This would be similar e.g. how a TimeSpan is implemented in the framework.

Alex
Now you mention it, I prefer that as well. That'll teach me to leap in with both feet when I should be going to bed instead!
Xav