Link to a full functional solution.
I'm working on a physics based algorithm, and I find myself working a lot with functions of style
double GetSpeed(double acceleration, double angle, double resistance)
A lot of physical values are passed from function to function as double, and it is a nightmare. I fail a lot at giving the right value at the right parameter, and also at the good units. Sometimes speed is in m/s, sometimes in km/h.
So my idea was to create an abstract class for different physical values, like Speed, Angle, etc...
Since you can add doubles, you can also add speeds or angles, so the abstract class must support adding, soustracting, all basic operations you can do on double.
So here is my abstract class :
public interface ISIQuantity
{
double SIValue { get; }
}
/// <summary>
/// Represent maths or physics quantities like angle, mass, speed
/// http://en.wikipedia.org/wiki/International_System_of_Units
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SIQuantity<T> : ISIQuantity, IComparable, IComparable<T>, IEquatable<T>
where T : ISIQuantity
{
private readonly double _value;
public double SIValue { get { return _value; } }
public SIQuantity(double value)
{
this._value = value;
}
public int CompareTo(T other) { return this.SIValue.CompareTo(other.SIValue); }
public int CompareTo(object other)
{
if (other is T)
{
return this.CompareTo(((T)other));
}
else
{
return 1;
}
}
public bool Equals(T other) { return this.SIValue.Equals(other.SIValue); }
public override bool Equals(object other)
{
if (other is T)
{
return this.Equals(((T)other));
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this._value.GetHashCode();
}
public static bool operator ==(SIQuantity<T> a, SIQuantity<T> b)
{
// If both are null, or both are same instance, return true.
if (Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(SIQuantity<T> r1, SIQuantity<T> r2)
{
return !(r1 == r2);
}
public static bool operator <(SIQuantity<T> r1, SIQuantity<T> r2)
{
return (r1.SIValue.CompareTo(r2.SIValue) < 0);
}
public static bool operator >(SIQuantity<T> r1, SIQuantity<T> r2)
{
return (r1.SIValue.CompareTo(r2.SIValue) > 0);
}
public static bool operator <=(SIQuantity<T> r1, SIQuantity<T> r2)
{
return (r1.SIValue.CompareTo(r2.SIValue) <= 0);
}
public static bool operator >=(SIQuantity<T> r1, SIQuantity<T> r2)
{
return (r1.SIValue.CompareTo(r2.SIValue) >= 0);
}
protected abstract T CreateFromSI(double siValue);
public static T operator +(SIQuantity<T> r1, SIQuantity<T> r2)
{
return r1.CreateFromSI(r1.SIValue + r2.SIValue);
}
// all kind of operator
}
Basically, just a wrapper around a double.
The double is called SIValue, like Standard Unit value. The idea is to keep the value in the unit of the standard system. So Kilogram for a mass, or meter per seconds for speed.
Each of the derived class must override the CreateFromSI
method for specify how to build a new unit based on a value in SI unit.
Here is how we use it :
public enum SpeedUnit { KilometerPerHour, MeterPerSecond }
public class Speed : SIQuantity<Speed>
{
protected override Speed CreateFromSI(double value)
{
return new Speed(value, SpeedUnit.MeterPerSecond);
}
public Speed(double value, SpeedUnit unit)
: base(unit == SpeedUnit.MeterPerSecond ? value : value / 3.6)
{
}
public static Speed FromKilometerPerHour(double value)
{
return new Speed(value, SpeedUnit.KilometerPerHour);
}
public static Speed FromMeterPerSecond(double value)
{
return new Speed(value, SpeedUnit.MeterPerSecond);
}
public double KilometerPerHour
{
get
{
return this.SIValue * 3.6;
}
}
public double MeterPerSecond
{
get
{
return this.SIValue;
}
}
}
A little extension to make life easier
public static Speed KilometersPerHour(this int speed)
{
return Speed.FromKilometerPerHour(speed);
}
The tests
[TestMethod]
public void Test_lt()
{
var speed1 = 36.KilometersPerHour();
var speed2 = 37.KilometersPerHour();
Assert.IsTrue(speed1 < speed2);
}
[TestMethod]
public void Test_lte()
{
var speed1 = 36.KilometersPerHour();
var speed2 = 37.KilometersPerHour();
Assert.IsTrue(speed1 <= speed2);
}
[TestMethod]
public void Test_lte_e()
{
var speed1 = 36.KilometersPerHour();
var speed2 = 36.KilometersPerHour();
Assert.IsTrue(speed1 <= speed2);
}
[TestMethod]
public void Test_gt()
{
var speed1 = 37.KilometersPerHour();
var speed2 = 36.KilometersPerHour();
Assert.IsTrue(speed1 > speed2);
}
[TestMethod]
public void Test_gte()
{
var speed1 = 37.KilometersPerHour();
var speed2 = 36.KilometersPerHour();
Assert.IsTrue(speed1 >= speed2);
}
[TestMethod]
public void Test_Add()
{
Assert.AreEqual(5.KilometersPerHour(), 3.KilometersPerHour() + 2.KilometersPerHour());
}
I have do the sames with the physicals value angle, force, acceleration, etc... So I can call my safe typed method like that :
Speed GetSpeed(Acceleration acceleration, Angle angle, Force resistance)
{
// foo algorihtm for prouving the point
var speed = Speed.FromMeterPerSeconds(acceleration.MeterPerSecondsMinus2 * angle.Degrees + resistance.Newton);
return speed;
}
Now, the questions :
First Point :
I have defined the SIQuantity as a generic type, so my abstract class contains all the logic for adding, substracting, compare the value, and return the good type. This way, it looks like a hack, since I have to define my generic type to extend an interface I have called ISIQuantity to access the SIValue. This looks weird.
Second Point : The mechanism to add two values is typically 'take two, unwrap, add, wrap, return'.
public static T operator +(SIQuantity<T> r1, SIQuantity<T> r2)
{
var v1 = r1.SIValue; // unwrap
var v2 = r2.SIValue;
var sum = v1 + v2; // add
var wrap = r1.CreateFromSI(sum); // wrap
return wrap; // return
}
I find it ugly.
The first thing is that each of the based class must define the method CreateFromSI. I tried to find a way to do it all in the abstract class, but since I can't define a constructor or something like that in the abstract class, I have used the kind of factory pattern to build the object.
Can I do it in a better way?
In a more general way, have I reinvented the wheel? I search a lot on something similar already existing, but I find nothing.
I use this code a lot, lot, and lot. It makes my life sooooo much easier. I don't fight anymore, all my algorithm is type safe, I'm confident on the unit of the physical values I use, I unwrap the values when it's time to really use them, like this little method :
public static Power operator *(Force force, Speed speed)
{
return Power.FromWatt(force.Newton * speed.MeterPerSecond);
}
[TestMethod]
public void TestMethod1()
{
// 10000W = 1000N * 10m/s
Assert.AreEqual(Power.FromKilowatt(10), Force.FromDecanewton(100) * 36.KilometersPerHour());
}
I use it so often that I must be sure it is perfect. So I definitely need a code review!
Speed.FromMeterPerSecond(force.Newton * speed.MeterPerSecond)
would work. I think it's not terrible to allow something like this, but it should not be the normal way you do calculations, if you're striving for compiler-enforced safety. \$\endgroup\$