2016-12-22 3 views
5

여러 수준의 게임을 디자인하고 있습니다. 나는 어떤 레벨을 설정해야 하는지를 나타내는 인수를 기반으로 보드를 설정하는 setup 클래스를 가지고있다. 다음은 클래스입니다 :적은 코드 중복으로 게임에 레벨을 어떻게 추가 할 수 있습니까?

public class BoardState { 
    public BoardState(InitialState state) { 
     switch (state) { 
      case EMPTY: 
       setupEmptyState(); 
       break; 
      case INTEGRATIONTEST: 
       setupIntegrationTestState(); 
       break; 
      case LEVEL_1: 
       setupLevelOne(); 
       break; 
      case LEVEL_2: 
       setupLevelTwo(); 
       break; 
      default: 
       throw new Error("Invalid level selection"); 
     } 
    } 

    private void setupEmptyState() { } 

    private void setupIntegrationTestState() { } 

    private void setupLevelOne() { } 

    private void setupLevelTwo() { } 
} 

이 잘 작동하지만 때마다 내가 세 곳에서 코드를 추가 할 필요가 새로운 수준의 추가 : InitialState 받아 국가의 목록을 정의 enumswitch 문을 생성자를 , 그리고 클래스의 본문에서 문제의 레벨을 설정하는 메소드를 추가해야합니다.

내가 유지하고 싶은 좋은 점 중 하나는 레벨 목록을 정의하는 enum을 기반으로 추가 한 각 레벨에 대해 새로운 GUI가 자동으로 채워진다는 점입니다.

새 레벨 추가와 관련된 오버 헤드가 줄어들도록이 코드를 리팩토링 할 수 있습니까?

+0

이것은 실제로 매우 광범위한 질문입니다. 거기에는 특별한 대답이 없습니다. 레벨 설정의 공통점에 따라 다릅니다. 우리는 게임의 본질을 모릅니다. 레벨 수가 유한 한 수준으로 설계 되었다면 이것이 가장 좋은 방법입니다. 그러면 그 레벨들 사이에 공통점이 무엇인지보고 그것을위한 방법을 만들고 초기화시 호출해야합니다. 무한한 레벨을 위해 디자인 되었다면, 전체 코드는 레크리에이션이 필요합니다. – Everyone

+0

'setupX()'메소드가 얼마나 비슷합니까? 코드를 게시 할 수 있습니까? – Spotted

+0

@ Spotted 매우 비슷합니다 - 링크 된 목록에 개체를 추가하기 만하면됩니다. 생성 된 객체의 수와 위치는 레벨에 따라 다릅니다. – nhouser9

답변

3

종종 코드 중복을 줄이려면 인터페이스가 발생합니다. 이 시간 (OP의 의견에 따라) 당신이 당신을 수준에 따라 보드에 다른 개체를 추가해야 할 것입니다 :

import java.util.List; 

public interface LevelSettings { 
    List<GameObject> startingObjects(); 
} 

이제 BoardState 그 (더 이상 setupX() 방법)과 같은

import java.util.List; 

public class BoardState { 
    private final List<GameObject> gameObjects; 

    public BoardState(LevelSettings settings) { 
     this.gameObjects = settings.startingObjects(); 
    } 
} 

당신은 ...

당신이 하나 열거의 인터페이스를 구현하여 두 세계 (인터페이스 및 ENUM)의 장점을 결합 할 수 있습니다 동적 GUI에 버튼을 생성하기 위해 enum을하는 것이 좋은 지정된 이후
import java.util.Arrays; 
import java.util.Collections; 
import java.util.List; 

public enum InitialState implements LevelSettings { 
    EMPTY { 
     @Override 
     public List<GameObject> startingObjects() { 
      return Collections.emptyList(); 
     } 
    }, 
    INTEGRATIONTEST { 
     @Override 
     public List<GameObject> startingObjects() { 
      GameObject g1 = new GameObject("dummy 1"); 
      GameObject g2 = new GameObject("dummy 2"); 
      return Arrays.asList(g1, g2); 
     } 
    }, 
    LEVEL_1 { 
     @Override 
     public List<GameObject> startingObjects() { 
      //read a config file to get the starting objects informations 
      //or also hardcoded (not preferred) 
     } 
    }, 
    LEVEL_2 { 
     @Override 
     public List<GameObject> startingObjects() { 
      //read a config file to get the starting objects 
      //or also hardcoded (not preferred) 
     } 
    }; 
} 

그리고 기본적으로입니다. LEVEL_3을 추가해야 할 경우 InitialState에 입력하면됩니다.당신이 확신하지 않는 경우

당신이 요청 된 것을 넘어 여기에서

더 한 단계를 가고,이 부분을 무시 주시기 바랍니다. 유연성에 더욱 코드 중복과 이득을 줄이기 위해 나는 단지 설정 파일에이 구성을 저장하는 것이 좋은 방법으로

:

그래서
import java.util.List; 

public enum InitialState implements LevelSettings { 
    EMPTY { 
     @Override 
     public List<GameObject> startingObjects() { 
      return readFromFile("empty.level"); 
     } 
    }, 
    INTEGRATIONTEST { 
     @Override 
     public List<GameObject> startingObjects() { 
      return readFromFile("integration_test.level"); 
     } 
    }, 
    LEVEL_1 { 
     @Override 
     public List<GameObject> startingObjects() { 
      return readFromFile("1.level"); 
     } 
    }, 
    LEVEL_2 { 
     @Override 
     public List<GameObject> startingObjects() { 
      return readFromFile("2.level"); 
     } 
    }; 

    private static List<GameObject> readFromFile(String filename) { 
     //Open file 
     //Serialize its content in GameObjects 
     //return them as a list 
    } 
} 

당신은 당신이 실제로 만하면 새로운 수준을 추가하기로 결정하는 경우 레벨 구성이 저장되어있는 파일 이름을 알고 있어야합니다.

또 다른 단계를가는

당신이 정말 까다 롭습니다 내가 너희를 생산 코드에서 사용하기 위해 조언을하지 않습니다 (하지만이 코드 중복 감소) 볼 것이다 무엇!

import java.util.List; 

public enum InitialState implements LevelSettings { 
    EMPTY, INTEGRATIONTEST, LEVEL_1, LEVEL_2; 

    @Override 
    public List<GameObject> startingObjects() { 
     return readFromFile(this.name() + ".level"); 
    } 

    private static List<GameObject> readFromFile(String filename) { 
     //Open file 
     //Serialize its content in GameObjects 
     //return them as a list 
    } 
} 

여기서 우리는 해당 올바른 파일을 찾기 위해 열거 형 이름 자체에 의존합니다. 이 코드는 규약에 기반하여 파일 이름이 ".level"확장명을 가진 열거 형 이름에 따라 지정 되었기 때문에 작동합니다. 새로운 레벨을 추가해야 할 때 열거 형에 추가하면됩니다. ...

+0

이것은 내가 원하는 모든 것을합니다. 세부 사항을 가지고 놀아 보겠다. 그러나 나는 분명히 내 게임에서 이와 유사한 것을 구현할 것이다. 정말 고마워! – nhouser9

2

상속을 사용할 수 있습니다. 다형성은 여기에있는 키워드입니다.

InitialState 클래스를 추상 기본 클래스 (또는 공용 필드가없는 경우 인터페이스)로 설정하고 public abstract void setup(); 메서드를 정의하십시오.

abstract class InitialState { 
    public abstract void setup(); 
} 

그런 다음, 원래의 스위치 각각의 경우, 예를 LevelOne를 들어, 기본 클래스에서 클래스를 파생하고 setup()를 재정 의하여 그 특정을 구현합니다.

class LevelOne extends InitialState { 
    @Override 
    public void setup() { 
     // The code from "setupLevelOne()" goes here 
    } 
} 

귀하의 BoardState 클래스는이에 감소 :

public class BoardState { 
    public BoardState(InitialState state) { 
     // At runtime, the right method of the actual 
     // state type will be called dynamically 
     state.setup(); 
    } 
} 

을하지만,이 게터에 액세스 할 수 있도록, public abstract void setup(BoardState boardState)로 설정 방법을 정의 고려하여 BoardState 클래스의 interal 상태를 설정해야하는 경우 setter 메서드.

이 계기는 다른 유형의 레벨에 대해 여러 개의 추상 레이어를 추가 할 수 있기 때문에 코드 재사용을 촉진 할 수도 있습니다.

+0

나는 대부분의 설정이'BoardState'에서 개인 상태와 관련이 있다고 생각합니다. – shmosel

+0

전체적으로 나쁘지는 않지만 한 가지 문제가 있습니다. 보드 상태를 나열하기 위해 'Enum'을 사용하여 각 레벨에 대한 버튼을 자동으로 채우도록 GUI를 설정했습니다. 솔루션으로 변경하면 새로운 레벨마다 여러 위치에서 코드를 편집해야합니다. 각 InitialState 구현에 대해 새 버튼을 추가하고 해당 버튼을 정의하여 올바른 구현을 작성해야합니다. 각 구현의'setup()'메소드를 작성할 필요가있다. 'InitialState'의 각 구현에 대해 버튼을 동적으로 생성하는 방법이 있다면, 이것은 완벽 할 것입니다. – nhouser9

+0

@ nhouser9 어이 거기에 새로운 요구 사항, 당신은 OP에서 지정하지 않았다! 제발 그것을 업데이 트하십시오. – Spotted

1

잘 모든 방법을 한 가지 방법으로 리 팩터링 할 수 있습니다. 레벨의 ID로 int를 취하고 각 레벨의 구조화 된 정보가 들어있는 JSON 파일을로드하고 주어진 레벨을 작성한다고 가정 해보십시오. 예를 들어 :

"levels" : [ 
    "level" : { 
     "id" : "001", 
     "size" : "200", 
     "difficulty" : "2" 
    }, 
    "level" : { 
     "id" : "002", 
     "size" : "300", 
     "difficulty" : "3" 
    } 
] 

다음 코드에서 : 다음

public void setupLevel(int id) throws levelNotFoundException{ 
    //somehow like this 
    Document doc = parse("levels.json"); 
    for(element elm: doc.get("levels")){ 
     if(Integer.parseInt(elm.get("id")).equals(id)){ 
      //setup your level 
     } 
    } 
} 

어딘가에 당신이 당신의 메서드를 호출 :

int levelId = getNextLevel(); 
try{ 
setupLevel(levelId); 
} catch (LevelNotFoundException e){e.printStackTrace();} 

또는 당신이 사용할 수있는 XML, 또는 단순히 하드 코드를 , 모든 레벨을 배열에 저장하십시오.