在Java中实现Factory Pattern的最佳方法

Mik*_*e B 5 java factory-pattern

我正在尝试编写工厂模式以在我的程序中创建MainMode或TestMode.我以前用来创建这些对象的代码是:

play = (isMode) ? new MainMode(numberRanges, numberOfGuesses) : 
                  new TestMode(numberRanges, numberOfGuesses, randNo());
Run Code Online (Sandbox Code Playgroud)

我的游戏(游戏)将根据布尔值(isMode)创建MainMode对象或TestMode对象.正如您所看到的,我在TestMode对象中添加了一个额外的值(randNo()).此值在TestMode中用于允许用户输入自己的"随机数",而在MainMode构造函数中,这是随机生成的.在这个程序中,MainMode和TestMode都是抽象类Game的子类.

现在我想用工厂模式替换这一行,虽然我不确定我的TestMode构造函数需要一个额外的对象,我不确定我需要传递这个值的位置.如果我要创建一个工厂,它需要在一个新的类中,可能名为GameFactory或ModeFactory或类似的东西.

我该怎么做?

编辑:这里的问题是上面的代码在我的GUI中,其中numberRanges,numberOfGuesses和randNo()方法的值是.我想创建一个Factory类,但我无法传递这些值,因为randNo()会激活它自己.这是我的randNo()方法.

private int randNo() {
    boolean isValidNumber = true;
    int testRandomNum = 0;
    while(isValidNumber) {
        try {
            testRandomNum = Integer.parseInt(JOptionPane.showInputDialog("Enter Random Number"));
            isValidNumber = false;
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(null, "Sorry, but the number you entered was invalid");
        }
    }

    return testRandomNum;
}
Run Code Online (Sandbox Code Playgroud)

问题是每当我传递randNo()时它都会显示JOptionPane.正如我已经说过的,GUI和Logic是分开的.GUI位于GUI包中,而其余代码位于逻辑包中.

tpd*_*pdi 12

请注意,其他一些答案可以说是工厂,但没有描述GOF工厂模式.

现在我想用工厂模式替换这一行,虽然我不确定我的TestMode构造函数需要一个额外的对象,我不确定我需要传递这个值的位置.

好吧,你可以这样想:MainMode,而不是TestMode,是一个特殊的东西.它所做的特殊事情是忽略给定的数字,以确保它真的是随机的.通过这种思考方式,MainMode可以做一些额外的事情.

或者,如果除了随机性之外,MainMode和TestMode没有区别,那么您可能会考虑将这种相似性分解为一个类,这是计算随机数的两种策略之一.一个策略实际上是随机的,一个是有悖常理的,随机范围只有1个值.

但是我们假设MainMode和TestMode之间存在其他差异 - 可能是TestMode向System.out输出额外的调试或其他东西.

我们仍然可以将"我们如何提供随机性"从我们测试或玩真实游戏中分解出来.这些是正交问题.

所以现在我们知道除了'模式所做的任何其他事情之外,它应该接受随机性策略.然后我们可以,例如,当你被告知标准平台随机不够随机时,你可以用更好的随机替换它.

或者您可以测试范围的范围仅限于两个选项,或者总是从一个替换为零,或者在每次调用时返回一些Vecrtor或Iterator中的下一个值.

因此,我们使用GOF策略模式来构建随机策略:

interface RandomStrategy { 
  public double random();
}

public class NotSoRandom implements RandomStrategy {
  private double r;
  public NotSoRandom( final double r ) { this.r = r; }
  public double random() { return r; }
}

public class PlatformRandom implements RandomStrategy {
  public double random() { return Math.random(); }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您的整个应用程序只创建一个'模式,那么就不需要工厂了; 当你需要反复创建相同的类类型时,你使用工厂; 事实上,工厂只是一种创造正确类型(子)类的策略.

在生产代码中,我使用了工厂,我有一些创建东西的泛型类,我需要告诉如何创建正确的子类来创建; 我通过工厂来做那件事.

现在我们为'Mode;创建一个Factory模式; 这将与战略模式惊人相似:

abstract class Mode() {
 private RandomStrategy r;
 public Mode( final RandomStrategy r ) { this.r = r; }
 // ... all the methods a Mode has
}

public class MainMode implements Mode {
  public MainMode( final RandomStrategy r ) { super(r); }
}

public class TestMode implements Mode {
  public TestMode( final RandomStrategy r ) { super(r); }
}

interface ModeFactory{ 
  public Mode createMode( final RandomStrategy r );
}

public class MainFactory() {
  public Mode createMode( final RandomStrategy r ) {
      return new MainMode(r);
  }
}

public class TestFactory() {
  public Mode createMode( final RandomStrategy r ) {
      return new TestMode(r);
  }
}
Run Code Online (Sandbox Code Playgroud)

所以现在您了解工厂模式和策略模式,以及它们在"形状"中是如何相似的,但它们的使用方式不同:工厂模式是对象创建并返回要使用的对象; 策略是对象行为,通常显式创建实例,并对实例进行引用,以封装算法.但就结构而言,它们非常相似.

编辑:OP在评论中询问"我如何将其集成到我的GUI中?"

嗯,这些都不属于你的程序的GUI,除了可能是'模式.您将创建ConcreteStrategy并在某些安装例程中将其传递给首选Factory,可能根据命令行参数或配置文件确定要使用哪个.基本上,当您在原始帖子中选择正确的班级时,您会选择正确的工厂.再说一遍,如果你只创造一件东西,你就不需要工厂; 工厂是大规模生产(或创建相关具体类型的家庭 - 虽然这超出了这个问题的范围).

(假设我们有一个游戏,用户可以在命令行上选择是否与机器人或龙战斗;然后我们想要实例化一个产生Opponents(一个接口)的OpponentFactory,派生类RobotOpponent和DragonOpponent,并传递该工厂对于spawnsNewOpponent()的游戏部分.同样,用户可能选择勇敢或懦弱的对手,我们将其设置为策略.我们不需要制作更多策略实例,因为策略通常是幂等的(无国籍和单身).)

static int main( String[] args ) {
// setup game world

final RandomStrategy r = "random".equals(args[0]) 
  ? new PlatformRandom() : new  NotSoRandom( Integer.intValue(args[0]) ) ;
// notice the simlarity to the code you originally posted;
// we factored out how to achieve "randomness" as a Strategy.

// now we will use our Strategy to setup our Factory;

final ModeFactory f = "test".equals(args[1])
  ? new TestFactory(r) : new MainFactory(r);
// also similar to your code
// we've just added an extra level of indirection: 
// instead of creating a Mode, we've created an object that can create Modes
//  of the right derived type, on demand.

// call something that uses our factory
functionThatRunsameAndNeedstoProduceModesWhenevertNeedsTo( f ); 

}
Run Code Online (Sandbox Code Playgroud)