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

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

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

我的游戏(游戏)将根据布尔值(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; } 

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

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

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

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

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

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

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

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

或者你可以测试randoms的范围仅限于两个选项,或者总是从一个替换为零,或者在每次调用时返回一些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(); } } 

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

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

现在我们为’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); } } 

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

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

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

(假设我们有一个游戏,用户可以在命令行上选择是否与机器人或龙战斗;然后我们想要实例化一个产生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 ); } 

工厂的重点是它应该具有适当创建游戏所需的状态。

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

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

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

试试像,

 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,然后创建私有构造函数。

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

就像是:

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

把这个方法放在合理的地方(这个很棘手,可能是一个静态的ModeFactory)

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

现在所有玩法都要调用ModeFactory.createMode(…)并传递相应的布尔值。

编辑(响应OP更新):

在调用实际构造函数之前,会对rand()进行求值,并显示GUI。 这就是激活自己的意思吗?

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

反过来说(模型调用你的GUI)比较棘手,可能是一个坏主意。

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

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

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

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

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

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

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

抽象类中的commoun逻辑可用于其下行程序:

 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 } 

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

  public class FileInputGridManager extends AbstractGridManager { private String filePath; @Override public Grid initGrid() { return this.initGrid(this.getFilePath()); } public Grid initGrid(String filePath) { List cells = new ArrayList<>(); char[] chars; File file = new File(filePath); // for ex foo.txt // ... more logic return grid; } } 

AbstractGridManager的接收器将调用他的方法并获取逻辑,在具体的下行程序(部分在抽象类方法中)中实现,而不知道他得到的具体实现是什么。 这也像控制或dependency injection的反转一样