0

I run this open-source library cleverly named the Unit Class Library. It's purpose, like many others is to allow for more intelligent handling of units (rounding, conversion, etc.) Here is an example of the Library at work

Distance distance = new Distance(DistanceType.Inch, 3);

Area area = distance * distance;

Obviously the * operator is overloaded in the Distance class like so:

public static Area operator *(Distance d1, Distance d2)
{
    return new Area(d1, d2);
}

In attempting to make use of the library in a real application, I have come to the need to do some complex equations using these types and am currently having to reduce all of my units back to arbitrary doubles to do the math:

Let's pretend we are calculating Volumetric Flow Rate

(This is just an example. The equations in my real application end up with Distance ^ 5th and other strange units due to integrals and derivatives.)

Volume v = new Volume(VolumeType.CubicMeters, 10); 
Time t = new Time(TimeType.Hours, 5);

double volumeAsDouble = v.CubicInches; 
double timeAsDouble = t.Seconds;

double flowRateAsDouble = volume / time;
VolumetricFlowRate flowRate = new VolumetricFlowRate(VolumetricFlowRateType.InchesCubedPerSecond, flowRateAsDouble);

This is an unpleasant solution because it causes unnecessary conversions. This introduces unnecessary rounding errors. And I know that I can make an operator that handles this.

Tangent

If you look at the rest of the library, you will see that conversion from the passed in value to a different unit is delayed until it is needed. This ensures that if you will always get back the same value as you input as long as it is in the same Unit as it was given. Like so:

Distance distance = new Distance(DistanceType.Inch, 0.0000000012345)

double inches = distance.Inches; //inches will equal 0.0000000012345

// other libraries will convert the input value as soon as the distance object is created.
// That would likely cause the number to be rounded. This library does not convert until a different unit is requested.

end Tangent

I would like to be able to do this:

VolumetricFlowRate flowRate = volume / time;

Without having to edit the Volume class to include a new operator.

I could go into the appropriate places and add a new operator for each new combination that should return VolumetricFlowRate when it is added to the library, but this is tedious and breaks the open closed principle.

Does anyone have a suggestion of a solution that is cleanly extended with each new type, but without breaking the open closed principle?

jth41
  • 3,808
  • 9
  • 59
  • 109

1 Answers1

-1

There is no way to avoid conversions from one unit to another without a-priori knowledge of the units of the variables.

In your example if you know that all distances are in meters then you could postpone the conversion to cubic inches per second to the last moment when you really need this value. The class 'VolumetricFlowRate' would then handle the conversion of cubic meters per second to cubic inches per second. Hence only one conversion is needed.

However this does not solve the problem of extra conversions when not all distances haven the same unit.

So in order to minimize unnecessary conversions you must use the a-priori information as much as possible.

  • This does not change a thing. You will still need the a-priori information to minimize the number of conversions. – Jack de Veer Mar 28 '15 at 12:14
  • The best way to proceed is to convert all values when they are created to standarized SI units and calculate with those values. The result of the calculation is in SI units and can then be converted to the appropriate unit. This can be done automatically if you implement symbolic calculation on the SI units so the final conversion factor is calculated saving you the trouble of building large conversion tables. By also using a multiplication factor on all values you can increase the accuracy of the conversions. A good instruction text on numerical analysis can show you how to do this. – Jack de Veer Mar 28 '15 at 12:26
  • I'm afraid you are still misunderstanding. I don't have a problem with how to create a new unit type and do the calculations, (which if you look at the library more closely is more accurate than the method you describe...) My problem is about finding a way to extend classes I have already written with new overloaded operator functionality without reediting the source file of a unit class that I have already "finished" – jth41 Mar 28 '15 at 14:45