如何设计可用于测试的伪随机数生成器模式?

时间:2015-01-03 16:56:39

标签: java design-patterns random software-design

我目前开发了一个相当大的应用程序,其中包括计算数学问题。计算是在类中进行的,我们将它们称为A和B,它们具有一些随机的final属性和一个(唯一的)final int id作为实例变量。

我希望能够在测试模式下运行程序,在每次执行中,随机变量完全相同,并允许我使用Junit测试将结果与手工计算进行比较。显然,我不希望在每次代码更改后重新计算我的示例解决方案(例如,如果插入了先前对prng的访问,这将移动所有随机数)。

请注意,我目前使用Java.util.Random作为prng,但我愿意接受建议。

现在的问题是:我应该如何构建实例化和访问prng?要指出问题,请注意以下是非常糟糕的方法:

  • 我无法将其作为 singleton 并从任何地方访问,因为这样,类中的随机数将取决于实例化的顺序。
  • 我显然无法使用硬编码种子实例化新的随机(种子),因为如果程序运行多次(在测试模式之外),数字不会有所不同。

我提出了以下解决方案(主要基于this answer),但不完全相同,因为编程语言和测试种子生成是不同的。

public class PRNGeneratorGenerator{

    //Make class a singleton
    private PRNGeneratorGenerator instance;
    private PRNGeneratorGenerator() {}
    public PRNGeneratorGenerator getInstance(){
        if (instance == null) instance = new PRNGeneratorGenerator();
        return instance;
    }

    //RandomNumberGenerator-Methods and Attributes
    private boolean isTestMode = false;
    private Random seedRng = new Random();

    public void setTestMode(boolean testMode){ 
       isTestMode = testMode; 
    }

    public Random getPseudorandomNumberGenerator(long testSeed){
      if(isTestMode) return new Random(testSeed); 
      return new Random(seedRng.nextLong());
    }
}

具有最终随机数的类(上面名为A和B)将如下所示:

public class A{
  private final static long CLASS_SEED = 872349;
  private final int randomNumberOne;
  private final int randomNumberTwo;
  private final int id;

  public A(int id){
    this.id = id;
    long testSeed = CLASS_SEED + id;
    Random rnd = PRNGeneratorGenerator.getInstance().getPseudorandomNumberGenerator(testSeed);
    randomNumberOne = rnd.nextInt();
    randomNumberTwo = rnd.nextInt();
  }
}

在测试模式下,我会在开始任何实例化之前调用PRNGeneratorGenerator.getInstance().setTestMode(true)

这是解决我在java中的问题的好方法还是这种方法有任何缺点?我已经阅读了许多类似的问题,但没有找到能回答我问题的同等问题。提前感谢您的回答。

1 个答案:

答案 0 :(得分:2)

我建议创建一个“PRNGeneratorGeneratorTest”类,它创建自己的PRNGeneratorGenerator本地实例进行测试。改组测试&是一个坏主意。生产代码有很多原因:例如令人困惑的是,很容易在代码中留下一些东西,这些代码可以在不需要的时候翻转模式等等。测试类还有其它好处,例如让你能够在1个版本中同时完成所有测试结果自动显示&保存

如果您不想制作测试类,至少将getPseudorandomNumberGenerator除以2(即制作测试方法):

public Random getPseudorandomNumberGenerator(){
    return new Random(seedRng.nextLong());
}

public Random getPseudorandomNumberGeneratorTest(long testSeed){
    return new Random(testSeed); 
}

..它不是伟大的练习,但它比改组测试和生产代码要好得多。