我正在设计一个多层次的游戏。我有一个安装类,它根据收到的参数设置电路板,它指示应该设置的电平。这是班级:
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
enum
定义接受状态列表,switch
语句在构造函数和类的主体中,我必须添加一个方法来设置相关级别。
我想要保留的一件好事是,我的GUI会根据定义级别列表的enum
为我添加的每个级别自动填充一个新按钮。
如何重构此代码,以便减少与添加新关卡相关的开销?
答案 0 :(得分: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上动态创建按钮很不错,因此可以通过在枚举中实现接口来结合两者的优点(接口和枚举)。
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
}
}
这里我们依靠枚举名称来找到相应的正确文件。此代码有效,因为它基于约定,文件使用&#34; .level&#34;相应地命名为枚举名称。延期。当您需要添加新级别时,只需将其添加到枚举即可...
答案 1 :(得分: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();
}
}
但是,如果您需要设置BoardState
类的内部状态,请考虑将设置方法定义为public abstract void setup(BoardState boardState)
,以便您可以访问其getter和setter方法。
这个appraoch也可以促进代码的重用,因为你可以为不同类型的级别添加几个抽象层。
答案 2 :(得分:1)
您可以将所有工作重构为一种方法。 假设它将一个int作为关卡的ID,同时它加载一个包含每个关卡的结构化信息的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,或者只是对其进行硬编码,并将所有级别存储在数组中