2009-04-02 6 views
5

내 프로그램에서 MainMode 또는 TestMode를 만들기 위해 팩토리 패턴을 쓰려고합니다. 내가 이전에 이러한 개체를 만드는 데 사용 된 코드는했다 :초보자 : Java 팩토리 패턴

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

내 게임 (놀이)는 MainMode 객체 또는 부울 값 (isMode)에 따라 TESTMODE 개체를 만드는 것 중 하나. 당신이 볼 수 있듯이, 나는 TestMode 객체 (randNo())에 추가 값을 추가하고있다. 이 값은 TestMode에서 사용되어 사용자가 자신의 "Random Number"를 입력 할 수있게 해주지 만 MainMode 생성자에서는 무작위로 생성됩니다. 이 프로그램에서 MainMode와 TestMode는 모두 abstract 클래스 Game의 하위 클래스입니다.

이제 TestMode 생성자가 여분의 객체를 요구할 때 확신 할 수 없지만이 값을 전달해야하는 위치가 확실하지 않더라도이 선을 팩토리 패턴으로 바꾸려고합니다. 팩토리를 만들려면 새로운 클래스에 있어야합니다. 아마도 GameFactory 또는 ModeFactory 또는 그 라인을 따르는 이름이 될 것입니다.

어떻게하면됩니까?

EDIT : 위의 코드는 numberRanges, numberOfGuesses 및 randNo() 메소드의 값이있는 내 GUI에 있습니다. 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와 로직은 분리되어 있습니다. GUI는 GUI 패키지에 있고 나머지 코드는 로직 패키지에 있습니다.

답변

12

기타 답변 중 일부는 공장을 설명 할 수도 있지만 GOF 팩토리 패턴을 설명하지 않음에 유의하십시오. 내 TESTMODE 생성자로 을 확실치 않지만

지금 나는 추가 개체를 필요로하는 공장 패턴이 라인을 교체 할 그리고 내가 이 값을 전달해야 할 경우 확실입니다.

글쎄, 당신은 이런 식으로 생각할 수 있습니다 : TestMode가 아닌 MainMode가 특별한 것을하는 것입니다. 그것이하는 특별한 일은 이 실제로 무작위임을 보장하기 위해 주어진 숫자를 무시한다는 것입니다. 그것에 대해 생각하는 방식으로, 뭔가 특별한 것을하는 것이 MainMode입니다.

또는 임의성, MainMode 및 TestMode가 다르지 않은 경우에는 유사성을 하나의 클래스로 제외 할 수 있다고 생각할 수 있습니다.이 유사성은 임의의 숫자를 계산하기위한 두 가지 전략 중 하나입니다. 하나의 전략은 실제로는 무작위 적이며, 하나의 전략은 단지 하나의 값만의 무작위 범위와 함께 비뚤어 질 것입니다.

MainMode와 TestMode 사이에 다른 점이 있다고 가정 해 봅니다. 아마도 TestMode가 System.out 또는 기타 항목에 추가 디버깅을 출력 할 것입니다.

우리는 여전히 우리는 "테스트하거나 실제의 게임을하고부터. 이들은 직교 문제는"어떻게 우리가 임의성을 제공 않는다 "밖으로 인수 분해 할 수 있습니다.

을 그래서 지금 우리가 알고있는 추가하여 그 그 밖에 어떤 모드는 무작위 전략을 받아 들여야합니다. 예를 들어 표준 플랫폼 무작위가 충분히 무작위가 아니라는 말을들을 때 더 나은 무작위로 대체 할 수 있습니다.

또는 randoms의 범위가 단지 두 가지 선택으로 제한되거나 항상 1에서 0으로 대체되는 테스트를 수행하거나 각 호출에서 일부 Vecrtor 또는 Iterat의 다음 값을 리턴합니다. 또는.전체 응용 프로그램은 오직 하나 '모드를 만드는 경우 공장에 대한 필요가 없습니다,

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

이제;

그래서 우리는 임의성 전략을 구축하기 위해 GOF 전략 패턴을 사용하여 동일한 클래스 유형을 반복해서 작성해야 할 때 팩토리를 사용합니다. 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); 
    } 
} 

,하지만 서로 다른 :이 놀라 울 전략 패턴과 유사합니다 공장 Pattern은 Object Creational이며 사용할 객체를 반환합니다. 전략은 Object Behavioral이며, 인스턴스는 대개 명시 적으로 생성되며 알고리즘을 캡슐화하기 위해 인스턴스에 대한 참조가 유지됩니다. 그러나 구조 측면에서 보면 매우 유사합니다.

편집 : OP는 "GUI에 어떻게 통합합니까?"라는 질문에 OP가 묻습니다.

글쎄,이 중 아무 것도 '모드'를 제외하고 프로그램의 GUI에 속하지 않습니다. ConcreteStrategy를 생성하여 일부 설정 루틴에서 기본 설정 팩토리에 전달하여 명령 줄 인수 또는 구성 파일을 기반으로 사용할 설정을 결정할 수 있습니다. 기본적으로 원래 게시물에서 올바른 클래스를 선택하면 올바른 팩토리를 선택하게됩니다. 다시 한번 말하지만, 뭔가를 만들면 Factory가 필요하지 않습니다. 공장은 대량 생산 용 (또는 관련 콘크리트 유형의 패밀리 작성 -이 질문의 범위를 벗어나지 만). 파생 클래스 RobotOpponent 및 DragonOpponent으로) 우리는 반대 (인터페이스를 생성 OpponentFactory를 인스턴스화 할 것;

(우리는 사용자가 로봇 또는 용 싸울 여부를 명령 줄에서 선택할 수있는 게임을 가정 마찬가지로, 한 사용자가 전략으로 세운 용감한 또는 겁쟁이의 상대를 선택할 수도 있습니다. 더 많은 Strategy 인스턴스를 만들 필요가 없습니다. 전략은 보통 (비 저장 및 싱글) 나무 등입니다.)

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

} 
+0

그러면 GUI에 어떻게 구현할 수 있습니까? –

0

코드가 공장 패턴으로 변경되었을 수 있습니다.

뭔가 같은 :

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

장소 어딘가에 분별이 방법

이 MainMode 및 TESTMODE 동일한 유형의 서브 타입 (서브 클래스 또는 구현하는 것이 가정 (이 하나, 어쩌면 정적 ModeFactory 까다 롭습니다) 모드 인터페이스)

이제 모든 플레이는 ModeFactory.createMode (...)를 호출하고 적절한 부울을 전달해야합니다. (OP 업데이트에 응답)

편집 : 실제 생성자를 호출하기 전에

귀하 랜드()는 평가됩니다, 그리고 GUI를 제공한다. 그것이 당신 스스로를 활성화 시켜서 의미하는 것입니까?

모드에 대한 결정을 내릴 위치에서 디자인을 결정해야합니다. GUI가 있고 모델이있는 경우 팩토리 메소드를 호출하기 전에 임의 생성 (및 팝업) 호출이 필요한지 여부를 알 수 있도록 GUI를 설계 한 다음 난수를 factory 메소드를 호출하고 올바른 생성자를 선택하게하십시오.

다른 방법으로 (모델 호출 GUI) 까다 롭고 나쁜 생각 일 수 있습니다.

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을 사용하도록 수정 한 다음 개인 생성자를 만들 수 있습니다.

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

지금 앱에서 somwhere,이 팩토리를 초기화 할 수 있으며, 게임을 만들 필요가 어떤 코드에 전달할 :

그래서 나는이 같은 공장을 구축 할 것입니다. 이 코드는 이제 모드가 무엇인지 걱정하지 않아도되고 추가 임의 매개 변수를 전달할 필요가 없습니다. 이는 잘 알려진 인터페이스를 사용하여 게임을 만듭니다. 필요한 모든 상태는 GameFactory 객체에 의해 내부화됩니다.

+0

저는 이것을 시도했습니다. 그러나 randNo()를 매개 변수로 추가한다면 JOptionPane을 호출 할 것입니다. 이것은 내가 원하지 않는 것입니다. . –

+0

이 경우, testMode가 true 인 경우 getGame()을 호출 할 때 임의의 숫자를 생성하는 팩토리가 실제로 필요합니다. 아이디어는 클라이언트 코드가 어떤 종류의 게임을 만들고 싶어하는지 신경 쓰지 않는 것입니다. 그 행동은 모두 공장에서 캡슐화됩니다. – levik

+0

그러면 JOptionPane을 어떻게 제공 할 수 있습니까? GUI 클래스에있는 randNo()의 개념은 JOptionPane을 호출하고 사용자가 임의의 숫자 대신 자신의 숫자를 입력하여 게임을 테스트 할 수 있기 때문입니다. 이것은 GUI를 호출하기 때문에 GUI 클래스에 있습니다. –

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

그래서 시작할 때 당신은 CR 적절한 모드 팩토리를 만들어 재생을 생성해야하는 곳으로 전달하십시오.

0

은 아주 간단하게, 는 항상 매개 변수가 단일 매개 변수로,이를 다른 "모드"에 대한 여러 매개 변수가있는 경우, null을 보내 캡슐화, 사용하지 않을 경우 매개 변수를 사용합니다. 그냥 팩토리 메소드 후, 당신을 위해 주어진 이름의 클래스를 만들 것이다 이것을 시도하는 경우

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

은 당신이 정말하고 싶은 당신에게 개체를 반환하는 공장을 만드는 것입니다 추상 클래스 또는 인터페이스 (theyr 구현 자 물론). factory 메소드에서 당신은 어떤 구현자를 선택할지를 결정한다. 당신은 추상 클래스를 선택한다면, 당신은 그것에 공통 로직을 구현할 수 있고, 다른 메소드는 구현되지 않을 것입니다 (추상 클래스 선언).당신은 구체적인 디 센더들이 필요에 따라 그것들을 구현하게 할 것입니다.

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 
} 

구체적인 구현은 추상적 인 선언 된 방법을 구현해야합니다

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의 수신자는 그 메소드를 호출하고 구체적인 디 센더에서 (그리고 추상 클래스 메소드에서) 구현 된 로직을 얻습니다. 그가 구현 한 구체적인 구현이 무엇인지 알 수 있습니다. 이것은 컨트롤 또는 의존성 삽입의 역전과 같은 것입니다.