nho*_*er9 5 java oop design-patterns code-duplication
我正在设计一个多层次的游戏.我有一个安装类,它根据收到的参数设置电路板,它指示应该设置的电平.这是班级:
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() { }
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,但每次我添加一个新的级别我必须在三个地方添加代码:InitialState enum它定义接受状态的列表,switch构造函数中的语句,以及类的主体,我必须添加一个方法设置有问题的级别.
我想要保留的一件好事是,我的GUI会根据enum定义级别列表自动填充我添加的每个级别的新按钮.
如何重构此代码,以便减少与添加新级别相关的开销?
通常,当您需要减少代码重复时,就会出现接口。这次(根据您在OP中的评论)似乎您需要根据您的级别向板上添加不同的对象:
import java.util.List;
public interface LevelSettings {
List<GameObject> startingObjects();
}
Run Code Online (Sandbox Code Playgroud)
现在,BoardState看起来像这样(没有更多的setupX()方法)
import java.util.List;
public class BoardState {
private final List<GameObject> gameObjects;
public BoardState(LevelSettings settings) {
this.gameObjects = settings.startingObjects();
}
}
Run Code Online (Sandbox Code Playgroud)
由于您还指定了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)
}
};
}
Run Code Online (Sandbox Code Playgroud)
基本上就是这样。如果您需要添加,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
}
}
Run Code Online (Sandbox Code Playgroud)
因此,当您决定添加新关卡时,您实际上只需要知道存储该关卡配置的文件名。
您将看到的内容确实很棘手,我不建议您在生产代码中使用它(但它减少了代码重复)!
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
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们依靠枚举名称本身来找到相应的正确文件。该代码之所以有效,是因为它基于以下约定:文件根据带有“.level”扩展名的枚举名称进行命名。当您需要添加新级别时,只需将其添加到枚举中即可......