6

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

play = (isMode) ? new MainMode(numberRanges, numberOfGuesses) : 
                  new TestMode(numberRanges, numberOfGuesses, randNo());

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

现在我想用工厂模式替换这一行,尽管我不确定因为我的 TestMode 构造函数需要一个额外的对象,而且我不确定我需要在哪里传递这个值。如果我要创建一个工厂,它需要在一个新类中,可能命名为 GameFactory 或 ModeFactory 或类似的东西。

我该怎么办?

编辑:这里的问题是上面的代码在我的 GUI 中,其中 numberRanges、numberOfGuesses 和 randNo() 方法的值是。我想创建一个工厂类,但我无法传递这些值,因为 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;
}

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

4

8 回答 8

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(); }
}

现在,如果您的整个应用程序只创建一个“模式”,则不需要工厂;当您需要一遍又一遍地创建相同的类类型时,您会使用工厂;工厂实际上只是一种创建正确类型(子)类的策略。

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

现在我们为 'Mode; 创建一个工厂模式;这将惊人地类似于策略模式:

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);
  }
}

所以现在您知道了工厂模式和策略模式,以及它们在“形状”上的相似之处,但在使用方式上却有所不同:工厂模式是创建对象并返回要使用的对象;策略是对象行为的,通常显式创建一个实例,并持有对该实例的引用,以封装算法。但就结构而言,它们非常相似。

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

好吧,这些都不属于你的程序的 GUI,除了可能的“模式”。您将创建 ConcreteStrategy 并将其传递给某些设置例程中的首选工厂,可能会根据命令行参数或配置文件确定要使用的工厂。基本上,您会选择正确的工厂,就像您在原始帖子中选择正确的课程一样。再说一次,如果你只创建一个东西,你就不需要工厂。工厂用于大规模生产(或创建相关具体类型的系列——尽管这超出了这个问题的范围)。

(假设我们有一个游戏,用户可以在命令行上选择是打机器人还是打龙;那么我们想要实例化一个产生 Opponents(接口)的 OpponentFactory,具有派生类 RobotOpponent 和 DragonOpponent,并通过该工厂到生成NewOpponent()的游戏部分。类似地,用户可能会选择勇敢或懦弱的对手,我们将其设置为策略。我们不需要制作更多的策略实例,因为策略通常是幂等的(无状态和单身)。)

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 ); 

}
于 2009-04-02T05:34:40.287 回答
1

工厂的全部意义在于它应该具有适当地创建游戏所需的状态。

所以我会建立一个这样的工厂:

public class GameFactory {
   private boolean testMode;

   public GameFactory(boolean testMode) {
     this.testMode = testMode;
   }

   public Game getGame(int numberRanges, int numberOfGuesses) {
     return (testMode) ? new MainMode(numberRanges, numberOfGuesses) : 
       new TestMode(numberRanges, numberOfGuesses, getRandom());
   }

   private int getRandom() {
     . . . // GUI code here
   }
}

现在,您可以在应用程序的某个位置初始化这个工厂,并将其传递给创建游戏所需的任何代码。这段代码现在不需要担心它是什么模式,也不需要传递额外的随机参数——它使用一个众所周知的接口来创建游戏。所有需要的状态都由 GameFactory 对象内部化。

于 2009-04-02T05:15:14.950 回答
0

您的代码可能会更改为工厂模式。

就像是:

public static Mode createMode(boolean isMainMode)
{
    if(isMainMode) return new MainMode(...);
    return new TestMode(...);
}

将此方法放在合理的地方(这个方法很棘手,可能是静态模式工厂)

这假设 MainMode 和 TestMode 是相同类型的子类型(子类或实现 Mode 接口)

现在所有需要做的就是调用 ModeFactory.createMode(...) 并传递适当的布尔值。

编辑(响应 OP 更新):

您的 rand() 在调用实际构造函数之前被评估,并呈现 GUI。这就是你说的激活自己的意思吗?

你必须在你想决定模式的地方做出设计决定。如果你有一个 GUI 并且你有一个模型,最好设计 GUI 以在调用工厂方法之前知道是否需要调用随机生成(和弹出),然后将随机数传递给工厂方法,让它选择正确的构造函数。

反过来(模型调用你的 GUI)会更棘手,而且可能是个坏主意。

于 2009-04-02T04:54:06.187 回答
0

尝试类似的东西,

abstract class ModeFactory {

    public static Mode getMode(isMode, numberRanges, numberofGuesses) {
        return isMode ? new MainMode(numberRanges, numberofGuesses) : new TestMode(numberRanges, numberOfGuesses, randNo());
    }

    public static Mode getMode(isMode, numberRanges, numberofGuesses, someNumber) {
        return isMode ? new MainMode(numberRanges, numberofGuesses) : new TestMode(numberRanges, numberOfGuesses, someNumber);
    }

}

该类是抽象的,只是为了停止初始化。您可以修改它以使用 final ,然后创建一个私有构造函数。

于 2009-04-02T04:55:54.047 回答
0
interface ModeFactory {
    Mode createMode(int numberRanges, int numberOfGuesses);
}

class MainModeFactory implements ModeFactory {
    Mode createMode(int numberRanges, int numberOfGuesses) {
        return new MainMode(numberRanges, numberOfGuesses);
    }
}

class TestModeFactory implements ModeFactory {
    Mode createMode(int numberRanges, int numberOfGuesses) {
        return new TestMode(numberRanges, numberOfGuesses, randNo());
    }
}

...

play = modeFactory.createMode(numberRanges, numberOfGuesses);

因此,在启动时,您创建适当的模式工厂,将其传递到需要创建播放的任何位置。

于 2009-04-02T05:53:56.107 回答
0

很简单,总是使用一个参数,如果不使用参数,发送null,如果你有其他“模式”的几个参数,将它们封装成一个参数。

于 2011-06-27T16:20:46.603 回答
0

如果您只是在使用工厂方法,那将为您创建一个给定名称的类,试试这个:

public static MyInterface createClass(String name) throws IllegalAccessException,
        InstantiationException, ClassNotFoundException {
    try {
        Class myClass = Class.forName(name);
        MyInterface myObj = (MyInterface) myObj.newInstance();
        return myObj;
    } catch (ClassNotFoundException ex) {
        logger.error("Could not find a class {}", name);
        throw ex;
    } catch (InstantiationException e) {
        logger.error("Class must be concrete {}", name);
        throw e;
    } catch (IllegalAccessException e) {
        logger.error("Class must have a no-arg constructor {}", name);
        throw e;
    }
}
于 2013-07-09T14:43:34.703 回答
0

你真正想做的是创建一个工厂,它返回一个抽象类或接口的对象(当然是他们的实现者)。然后在您决定的工厂方法中,选择哪个实现者。如果您选择一个抽象类,您可以在其中实现一些通用逻辑,并让其他方法未实现(将它们声明为抽象)。您可以让具体的下降者根据他们的需要来实现它们。这是工厂设计模式:

public class GridManagerFactory {
    public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){
        AbstractGridManager manager = null;

        // input from the command line
        if(args.length == 2){
            CommandLineGridManager clManager = new CommandLineGridManager();
            clManager.setWidth(Integer.parseInt(args[0]));
            clManager.setHeight(Integer.parseInt(args[1]));
            // possibly more configuration logic
            ...
            manager = clManager;
        } 
        // input from the file
        else if(args.length == 1){
            FileInputGridManager fiManager = new FileInputGridManager();
            fiManager.setFilePath(args[0]);
            // possibly more method calls from abstract class
            ...
            manager = fiManager ;
        }
        //... more possible concrete implementors
        else{
            manager = new CommandLineGridManager();
        }
        manager.setLifecicleAlgorithm(lifecicleAlgorithm);
        return manager;
    }
}

抽象类中的公共逻辑可供其后代使用:

public abstract class AbstractGridManager {
    private LifecicleAlgorithmIntrface lifecicleAlgorithm;
    // ... more private fields

    //Method implemented in concrete Manager implementors 
    abstract public Grid initGrid();

    //Methods common to all implementors
    public Grid calculateNextLifecicle(Grid grid){
        return this.getLifecicleAlgorithm().calculateNextLifecicle(grid);
    }

    public LifecicleAlgorithmIntrface getLifecicleAlgorithm() {
        return lifecicleAlgorithm;
    }
    public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) {
        this.lifecicleAlgorithm = lifecicleAlgorithm;
    }
    // ... more common logic and geter-seter pairs
}

具体实现者只需要实现声明为抽象的方法:

  public class FileInputGridManager extends AbstractGridManager {

        private String filePath;

        @Override
        public Grid initGrid() {
            return this.initGrid(this.getFilePath());
        }

        public Grid initGrid(String filePath) {
            List<Cell> cells = new ArrayList<>();
            char[] chars;
            File file = new File(filePath); // for ex foo.txt
            // ... more logic
            return grid;
        }
    }

AbstractGridManager 的接收者会调用他的方法并获取逻辑,在具体的后代(以及部分抽象类方法)中实现,而不知道他得到的具体实现是什么。这也称为控制反转或依赖注入

于 2014-06-03T16:24:45.457 回答