In the code below, the primary purpose of class MyRNG
is to create a single method getMyRandom()
that will return a random number from any of several very different distributions and generators. The actual distribution and generator will be determined by information read from a file, although in the test program below, the information is simply stated in main()
at the point where the call to MyRNG()
occurs. In the code as it stands, a line (read from a file, console or command line) is parsed to extract the type of RNG wanted and the parameters for the distribution. Distributions are limited in the sample code to a Gaussian distribution, uniform distribution, and random choice
I think that the code is horrible (not surprising given that I'm an OOP novice) in that I use an enum
to signal which kind of RNG I really want, and then I use switch
statements to ensure that getMyRandom()
actually calls the correct kind of basic generator. I think I should be able to solve in another way! I have been reading about design patterns and I've convinced myself that it should be possible to greatly improve the code by using either Factory Method or Abstract Factory but I cannot see how to do it.
Questions that I'm struggling with ...
- What design pattern should I be focusing on?
- Most importantly for my understanding, what would a sketch of that design pattern look like in the context of my problem
- At the moment I have "this.rng = new Random()" within
class MyRNG
class where (I believe) it is called for each new MyRNG object. Does this make sense or should it be somewhere else, either in the code as it stands or in a relevant design pattern. - Where should the parsing of the RNG description go?
import java.util.Random;
class MyRNG {
// The DistributionType enum is used to keep track of the particular kind of RNG that
// we want so that the appropriate specific method can be called by the intermediary
// getMyRandom() method.
enum DistributionType {
UNIFORMDOUBLE, // Types of distribution to account for. ** VERY **Incomplete list.
GAUSSIAN,
CHOICE
};
Random rng;
DistributionType iAmThisTypeOfRNG; // Keeps track of what kind of RNG is ultimately called
String description;
// Constructor
public MyRNG (String description) {
this.rng = new Random();
this.description = description;
// Parse the description to determine what kind of RNG is really wanted
if (description.matches("UNIFORMDOUBLE +-?\\d+\\.?\\d+ +-?\\d+\\.?\\d+")) {
// For doubles on a unform distribution use UNIFORMDOUBLE lowerbound upperbound
// ... UNIFORMDOUBLE 18.3 22.2
this.iAmThisTypeOfRNG = DistributionType.UNIFORMDOUBLE;
} else if (description.matches("GAUSSIAN +\\d+\\.?\\d+ +\\d+\\.?\\d+")) {
// For Gaussian distribution use GAUSSIAN mean std
// ... GAUSSIAN 2.0 3.5
this.iAmThisTypeOfRNG = DistributionType.GAUSSIAN;
} else if (description.matches("CHOICE -?\\d+\\.?\\d+( +-?\\d+\\.?\\d+)*")) {
// For CHOICE distribution use CHOICE n1 n2 ...
// ... CHOICE 4.6 -2.35 1.8 -4.0 -1.9
this.iAmThisTypeOfRNG = DistributionType.CHOICE;
} else {
System.out.println("Error: Could not parse parameter string: " + description);
}
}
public double getMyRandom() {
double myRand;
double[] parameters = parametersFromDescription(description);
switch (iAmThisTypeOfRNG) {
case UNIFORMDOUBLE: // double on uniform distribution
myRand = myNextUniformDouble(rng, parameters);
// Next line for debugging info only
System.out.println("Parsed UNIFORMDOUBLE with description: " + description);
break;
case GAUSSIAN: // doubles from Gaussian distribution
myRand = myNextGaussian(rng, parameters);
// Next line for debugging info only
System.out.println("Parsed GAUSSIAN with description: " + description);
break;
case CHOICE: // Random choice from a list of doubles
myRand = myNextChoice(rng, parameters);
// Next line for debugging info only
System.out.println("Parsed CHOICE with description: " + description);
break;
default:
myRand = 0;
System.out.println("Non-existent kind of random string requested.");
}
return myRand;
}
public String toString() {
return "This MyRNG object has kind: " + iAmThisTypeOfRNG;
}
static double[] parametersFromDescription(String description) {
String[] parameterString = description.split("\s+");
double [] parameters = new double[parameterString.length - 1];
int j = 0;
for (int i = 1; i < parameterString.length; i++) {
parameters[j] = Double.parseDouble(parameterString[i]);
j++;
}
return parameters;
}
static double myNextUniformDouble(Random rng, double[] parameters) {
double lowerBound = parameters[0];
double upperBound = parameters[1];
return (upperBound-lowerBound) * rng.nextDouble() + lowerBound;
}
static double myNextChoice(Random rng, double[] parameters) {
int selection = rng.nextInt(parameters.length);
return parameters[selection];
}
static double myNextGaussian(Random rng, double[] parameters) {
double mean = parameters[0];
double standardDeviation = parameters[1];
return standardDeviation * rng.nextGaussian() + mean;
}
}
public class Main {
public static void main(String[] args) {
MyRNG rsA = new MyRNG("GAUSSIAN 5.66 1.2");
System.out.println(rsA);
System.out.println("Result of getMyRandom on rsA is : " + rsA.getMyRandom());
System.out.println();
MyRNG rsB = new MyRNG("UNIFORMDOUBLE -2.0 2.0");
System.out.println(rsB);
System.out.println("Result of getMyRandom on rsB is : " + rsB.getMyRandom());
System.out.println();
MyRNG rsC = new MyRNG("CHOICE 2.0 4.0 6.0 8.0 10.0 12.0");
System.out.println(rsC);
System.out.println("Result of getMyRandom on rsC is : " + rsC.getMyRandom());
System.out.println(rsC.getMyRandom());
}
}