Scalable solution to intelligent operators for unit classes

115 views Asked by At

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?

1

There are 1 answers

3
Jack de Veer On

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.