i want to not repeat myself (DRY), but i cannot have a single piece of code. For example here is code repeated 3 times with the same bug:
class StarWars : Movie
{
//Calculate "base ^ exponent"
public float Power(float base, float exponent)
{
return (base * exponent);
}
}
class Customer: Object
{
//Calculate "base ^ exponent"
public float Exponential(float base, float exponent)
{
return (base ^ exponent);
}
}
class Student: Person
{
//Calculate "base ^ exponent"
public float CalculateExpoential(float base, float exponent)
{
return CalculateExponential(2.7182818, exponent * Ln(base));
}
}
Now ideally i would have extracted this common function into it's own helper somewhere:
class LibraryOfHelperCode
{
public static float Exponentiation(float base, float exponent)
{
return Exp(2.71828183, base * Ln(exponent));
}
}
And converted the existing code to use it:
class StarWars : Movie
{
//Calculate "base ^ exponent"
public float Power(float base, float exponent)
{
return LibraryOfHelperCode.Exponentiation(base, exponent);
}
}
class Customer: Object
{
//Calculate "base ^ exponent"
public float Exponential(float base, float exponent)
{
return LibraryOfHelperCode.Exponentiation(base, exponent);
}
}
class Student: Person
{
//Calculate "base ^ exponent"
public float CalculateExpoential(float base, float exponent)
{
return LibraryOfHelperCode.Exponentiation(base, exponent);
}
}
The value is that now i've extracted the repeated code from
- Power
- Exponential
- CalculateExpoential
into a single function. This means that if there are any bugs, they only have to be fixed once. Which is good in this case, because there is a bug:
public float CalculateExpoential(float base, float exponent)
{
//19971012: Oops, should be natrual log of base, not exponent
return Exp(2.71828183, exponent * Ln(base));
}
and a few years after that:
public float CalculateExpoential(float base, float exponent)
{
//19990321: Oops, need to handle when exponent is zero
if (exponent == 0)
return 1.0;
//19971012: Oops, should be natrual log of base, not exponent
return Exp(2.71828183, exponent * Ln(base));
}
and later on:
public float CalculateExpoential(float base, float exponent)
{
//19990321: Oops, need to handle when exponent is zero
if (exponent == 0)
return 1.0;
//20040523: Another special case
if (Base = 0.0) && (Exponent > 0.0) then
return 0.0; // 0**n = 0, n > 0
//19971012: Oops, should be natrual log of base, not exponent
return Exp(2.71828183, exponent * Ln(base));
}
and finally:
public float CalculateExpoential(float base, float exponent)
{
//20101027: Microsoft just release a method in .NET framework 4.0 that does
//what we need. Use it:
return Math.Pow(base, exponent);
}
And everyone gets the fixes. On the other hand, i cannot guarantee that any one of those increment fixes won't break existing code.
Imagine a guy was calling:
char ps = Math.Trunc(Exponential(ProblemSize, ProblemComplexity));
and was never expecting the value to be larger than 128. He was wrong. And while the code was wrong all this time: it happened to work.
Now i come along and fix things, and suddenly code is crashing due to overflow and/or wraparound.
The problem i'm facing today is that a change in DRY common code affects everywhere it's used. The only acceptable (polotical) solution is to keep a copy of the library class for every executable/moduble/namespace/class that uses it.
Undoing any DRYness.
Is there any way out of this mess? When i can not repeat myself, but continue to get fixes and improvements as they are added to the single DRY code?
i mean...should i have shared code, but branch it at every release? But the issue that is polotically nobody wants the code every to be reverse-integrated.