Is it a bad idea to add a new method (that implements a new algorithm) to a class library for its new version and expose a version option the consumers can select?
It is just for backward compatibility where an old application might need a constant sequence of random numbers for a certain seed number.
The following code might speak my idea much better. Hopefully you get the essence.
//Class Library:
namespace MyGenerator
{
public sealed class MyRandom
{
public enum Version { One, Two }// add more options in the future
private int current;
private Version version;
public MyRandom(int seed, Version version = Version.One)
{
current = seed;
this.version = version;
}
public int Next()
{
switch (version)
{
case Version.One:
AlgorithmOne();
break;
case Version.Two:
AlgorithmTwo();
break;
// add more options in the future
}
return current;
}
private int AlgorithmOne()
{
current = (current * 1234567890 + 987654321) & 0x7FFFFFFF;
return current;
}
private int AlgorithmTwo()
{
current = (current * current + current) & 0x7FFFFFFF;
return current;
}
// add more algorithm in the future
}
}
and
//Console Application (for testing purposes):
using MyGenerator;
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int seed = 12345;
MyRandom rnd = new MyRandom(seed, MyRandom.Version.Two);
Console.WriteLine(rnd.Next());
Console.ReadLine();
}
}
}
No, never do this. If you want to design for future versionability then use the object-oriented patterns specifically designed for that. For example, here’s how I would solve your problem:
abstract class MyRandom
{
// Ensure that a third party cannot extend the base
// class by making the ctor private.
private MyRandom() {}
public abstract int Next();
private class First : MyRandom
{
// whatever
}
private class Second : MyRandom
{
// whatever
}
// Chef's choice:
public static MyRandom MakeRandom() { return new Second(); }
// Diner's choice:
public static MyRandom MakeFirst() { return new First(); }
public static MyRandom MakeSecond() { return new Second(); }
}
When you want to add a new version, that’s a private implementation detail of the class, and you then add a new static factory method. If the caller wants you to choose the best version, they call the method that lets you decide. If they have a specific version they want, they call a method that gives them that.
Some more guidance:
-
If you believe you will be adding more members to an enum in the future then you are probably doing something wrong. The set of items in an enum should be identical forever.
-
If you are taking a non-flags enum with n options and doing n different things as a result then just write n methods.
6
Having a version parameter would be dirty.
It’s a blatant mixing of concerns. The first concern is what the method is expected to do. The second concern how to manage multiple conflicting versions of a massive API. The two are completely unrelated and should stay as separate as possible.
As a developer, I’m only interested in the first concern (what the method is expected to do). If I try to call Math.Random and suddenly there’s a parameter for version… well, I’ll be confused. What does versioning have to do with randomness?
In other words, adding a version parameter would force the developer to become aware of the implementation of Math.Random (and its history) instead of just knowing what it’s expected to do. In my opinion, that would make it a very bad API.
2
The likely answer is… because they don’t want to.
Sounds like they are leaving their option open to switch to a different PRNG algorithm if a new one they prefer was to come along. There’s also the possibility that future .NET runtimes for entirely new platforms may require something different in terms of a PRNG algorithm, so they don’t want to lock themselves into a certain algorithm.
They don’t need to provide prior versions of their generators because they are flat out telling you not to rely on it for predictable results. The purpose is to return a psuedo-random number. Not a reproducible sequence of numbers. There is no reason to ever care what version of algorithm they are using, because you are calling it to get a random number.
If you need predictable results, you should implement your own random number generator (preferably using one of the better tested, known algorithms).
1
It’s an interesting idea. However, I would probably curse the library developer when I try to use a method like this. From user perspective, it makes the library much more complicated to understand.
In this specific case, I will go for the Template Method pattern. I will create an abstract class “Random” and make the existing “MyRandom” its subclass.
After this, I can implement algorithm B in another new subclass (“YourRandom”, “HisRandom” 🙂 ) The existing code will not break, new usage can select the algorithm for random.
1