- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我在创建清晰简洁的代码时遇到了困难,这些代码允许我使用各种命令来执行各种不同的操作。例如,在我正在开发的 N 体模拟器中,我想要的功能是用户可以输入诸如 tele pos [x] [y] [z]
或 tele celobj [celestial object name]
这样的命令。
为此,我根据空格的位置将输入字符串划分为标记数组。然后,我使用一系列 switch 语句,使得第一个单词 (tele
) 在一层 switch 语句中处理,然后第二个单词 (pos
或 celobj
) 在第二层 switch 语句中处理。然后相应地处理下一个 token 。通过所有这些不同的层,我检查用户是否输入了有效的单词数,以避免超出范围的异常。
我的代码工作正常,但显然它很难阅读并且过于复杂。我并不是在寻找代码来帮助我,而是寻找用于组织命令系统或以最佳方式设置逻辑的概念策略。
我已经包含了我的源代码以防万一,但我希望我的描述足够清楚。
public static void process(String cmd) {
String tokenNotFound = "Token not recognized...";
String notEnoughInfo = "Not enough info given. Please specify...";
String unableToParse = "Unable to parse number...";
String[] tokens = cmd.toLowerCase().split("\\s+");
switch (tokens[0]) {
case "close":
run = false;
break;
case "toggle":
if (tokens.length >= 2) {
switch (tokens[1]) {
case "render":
render = !render;
System.out.println("Render setting set to " + render);
break;
case "physics":
updatePhysics = !updatePhysics;
System.out.println("Physics update setting set to " + updatePhysics);
break;
case "trails":
showTrails = !showTrails;
System.out.println("Show trails setting set to " + showTrails);
break;
case "constellations":
showConstellations = !showConstellations;
System.out.println("Show constellations setting set to " + showConstellations);
break;
default:
System.err.println(tokenNotFound);
}
} else
System.err.println(notEnoughInfo);
break;
case "get":
if (tokens.length >= 2) {
switch (tokens[1]) {
case "fps":
System.out.println("FPS: " + realFPS);
break;
case "ups":
System.out.println("UPS: " + realUPS);
break;
case "cps":
System.out.println("CPS: " + realCPS);
break;
case "performance":
System.out.println("FPS: " + realFPS + " UPS: " + realUPS + " CPS: " + realCPS);
break;
case "time":
System.out.println(getTimestamp());
break;
case "celobj":
if (tokens.length >= 3) {
boolean objFound = false;
CelObj chosenObj = null;
for (CelObj celObj : physics.getCelObjs()) {
if (celObj.getName().toLowerCase().equals(tokens[2])) {
objFound = true;
chosenObj = celObj;
}
}
if (objFound) {
if (tokens.length >= 4) {
switch (tokens[3]) {
case "pos":
Vec3d pos = chosenObj.getCelPos();
System.out.println("POSITION: X= " + pos.x + " Y= " + pos.y + " Z= " + pos.z);
break;
case "vel":
Vec3d vel = chosenObj.getCelVel();
if (tokens.length >= 5 && tokens[4].equals("mag"))
System.out.println("VELOCITY: V= " + vel.magnitude());
else
System.out.println("VELOCITY: X= " + vel.x + " Y= " + vel.y + " Z= " + vel.z);
break;
case "mass":
System.out.println("MASS: M= " + chosenObj.getMass());
break;
case "radius":
System.out.println("RADIUS: R= " + chosenObj.getRadius());
break;
default:
System.err.println(notEnoughInfo);
}
} else
System.err.println(notEnoughInfo);
} else
System.err.println(tokenNotFound);
} else {
//Print list of celObjs
StringBuilder celObjNames = new StringBuilder("Celestial Objects: \n");
for (CelObj celObj : physics.getCelObjs()) {
celObjNames.append('\t').append(celObj.getName()).append('\n');
}
System.out.println(celObjNames.toString());
}
break;
default:
System.err.println(tokenNotFound);
}
} else
System.err.println(notEnoughInfo);
break;
case "set":
if (tokens.length >= 2) {
switch (tokens[1]) {
case "cps":
if (tokens.length >= 3) {
try {
int newCPS = parseInt(tokens[2]);
realTime_to_simTime = newCPS * timeInc;
System.out.println("Target CPS set to " + newCPS);
System.out.println("The simulation time is " + realTime_to_simTime + " times the speed of real time");
} catch (Exception e) {
System.err.println(unableToParse);
}
} else
System.err.println(notEnoughInfo);
break;
case "scale":
if (tokens.length >= 3) {
try {
scale = parseFloat(tokens[2]);
System.out.println("Render object scale is now set to " + scale);
} catch (Exception e) {
System.err.println(unableToParse);
}
} else
System.err.println(notEnoughInfo);
break;
case "speed":
if (tokens.length >= 3) {
try {
speed = parseFloat(tokens[2]);
System.out.println("Speed is now set to " + speed);
} catch (Exception e) {
System.err.println(unableToParse);
}
} else
System.err.println(notEnoughInfo);
break;
case "record":
if (tokens.length >= 4) {
if (tokens[3].equals("period")) {
try {
int newCPS = parseInt(tokens[2]);
realTime_to_simTime = newCPS * timeInc;
System.out.println("Target CPS set to " + newCPS);
System.out.println("The recording period is now every " + realTime_to_simTime + " seconds");
} catch (Exception e) {
System.err.println(unableToParse);
}
} else
System.err.println(tokenNotFound);
} else
System.err.println(notEnoughInfo);
break;
case "center":
if (tokens.length >= 3) {
boolean objFound = false;
CelObj chosenObj = null;
for (CelObj celObj : physics.getCelObjs()) {
if (celObj.getName().toLowerCase().equals(tokens[2])) {
objFound = true;
chosenObj = celObj;
}
}
if (objFound) {
centerCelObj = chosenObj;
System.out.println(chosenObj.getName() + " has been set as the center");
} else
System.err.println(tokenNotFound);
} else
System.err.println(notEnoughInfo);
break;
default:
System.err.println(tokenNotFound);
}
} else
System.err.println(notEnoughInfo);
break;
case "create":
//TODO:
break;
case "uncenter":
centerCelObj = null;
System.out.println("There is currently no center object");
break;
case "tele":
if (tokens.length >= 2) {
switch (tokens[1]) {
case "pos":
if (tokens.length >= 5) {
try {
double x = parseDouble(tokens[2]);
double y = parseDouble(tokens[3]);
double z = parseDouble(tokens[4]);
Vec3f cameraPos = new Vec3f((float) x, (float) y, (float) z);
//If camera is locked to an object, then translating the camera will only
//do so with respect to that planet
//Hence, the camera is translated back to world coordinates by translating it
//the negative of its locked celObj position vector
if (camera.getLockedCelObj() != null) {
cameraPos.translate(
new Vec3f(
camera.getLockedCelObj().getCelPos()
).negate()
);
}
camera.setPosition(multiply(worldunit_per_meters, cameraPos));
System.out.println("The camera position has been set to X= " + x + " Y= " + y + " Z= " + z);
} catch (Exception e) {
System.err.println(unableToParse);
}
} else
System.err.println(notEnoughInfo);
break;
case "celobj":
if (tokens.length >= 3) {
boolean objFound = false;
CelObj chosenObj = null;
for (CelObj celObj : physics.getCelObjs()) {
if (celObj.getName().toLowerCase().equals(tokens[2])) {
objFound = true;
chosenObj = celObj;
}
}
if (objFound) {
Vec3f celObjPos = new Vec3f(chosenObj.getCelPos());
Vec3f cameraPos = add(celObjPos, new Vec3f(0, (float) chosenObj.getRadius() * 2, 0));
//If camera is locked to an object, then translating the camera will only
//do so with respect to that planet
//Hence, the camera is translated back to world coordinates by translating it
//the negative of its locked celObj position vector
if (camera.getLockedCelObj() != null) {
cameraPos.translate(
new Vec3f(
camera.getLockedCelObj().getCelPos()
).negate()
);
}
//Make player 1 planet radius away from surface
camera.setPosition(multiply(worldunit_per_meters, cameraPos));
camera.setLookAt(multiply(worldunit_per_meters, celObjPos));
System.out.println("The camera position has been set to X= " + cameraPos.x + " Y= " + cameraPos.y + " Z= " + cameraPos.z);
} else
System.err.println(tokenNotFound);
} else
System.err.println(notEnoughInfo);
break;
default:
System.err.println(tokenNotFound);
}
} else
System.err.println(notEnoughInfo);
break;
case "lock":
if (tokens.length >= 2) {
boolean objFound = false;
CelObj chosenObj = null;
for (CelObj celObj : physics.getCelObjs()) {
if (celObj.getName().toLowerCase().equals(tokens[1])) {
objFound = true;
chosenObj = celObj;
}
}
if (objFound) {
camera.setLockedCelObj(chosenObj);
camera.setPosition(new Vec3f(0, 0, 0));
System.out.println("The camera has been locked to " + chosenObj.getName());
System.out.println("Type 'unlock' to revert back to unlocked status");
} else
System.err.println(tokenNotFound);
} else
System.err.println(notEnoughInfo);
break;
case "unlock":
String celObjName = camera.getLockedCelObj().getName();
//If camera is locked to an object, then translating the camera will only
//do so with respect to that planet
//Hence, the camera is translated back to world equivalent of where it is in
//that celObj's space by translating it the celObj's position
camera.setPosition(
add(
multiply(worldunit_per_meters,
(new Vec3f(camera.getLockedCelObj().getCelPos()))),
camera.getPosition()
)
);
camera.setLockedCelObj(null);
System.out.println("The camera has been unlocked from " + celObjName);
Vec3f pos = camera.getPosition();
System.out.println("The camera position has been set to X= " + pos.x + " Y= " + pos.y + " Z= " + pos.z);
break;
case "lookat":
if (tokens.length >= 3) {
switch (tokens[1]) {
case "celobj":
boolean objFound = false;
CelObj chosenObj = null;
for (CelObj celObj : physics.getCelObjs()) {
if (celObj.getName().toLowerCase().equals(tokens[2])) {
objFound = true;
chosenObj = celObj;
}
}
if (objFound) {
camera.setLookAt(new Vec3f(multiply(worldunit_per_meters, chosenObj.getCelPos())));
System.out.println("The camera is now looking at " + chosenObj.getName());
} else
System.err.println(tokenNotFound);
break;
}
} else
System.err.println(notEnoughInfo);
break;
default:
System.err.println(tokenNotFound);
}
}
最佳答案
你的直觉是对的。您拥有的代码确实可以通过以某种方式分解成更小的部分而受益。实现这一目标的一个好方法是使其更加由数据驱动。对一长串命令进行编码的一种方法是使用 switch 语句,但问题是命令越多,语句就会变得越来越长。数据驱动方法将命令名称及其背后的代码视为数据,并将命令列表与解析和执行命令的代码分开。
让我们从一个代表命令处理程序的简单界面开始。这是一个函数,它接受命令的参数,然后执行命令执行的任何操作。
public interface CommandHandler {
public void handle(List<String> arguments);
}
然后让我们让 process()
函数成为数据驱动的。现在,我们来处理前两个命令,“关闭”和“切换”。我们将从简单的开始,看看这个想法是否有意义,然后在我们大致了解我们想要做什么后充实实现。
我们将创建一个命令名称与其处理程序的映射。这将为我们提供一个紧凑的命令列表,每个命令背后的代码分为单独的回调函数。如果您不熟悉,Commands::close
是一个方法引用。它为我们提供了一个 CommandHandler
对象,该对象调用 Commands.close()
方法,我们稍后将定义该方法。
public static void process(String input) {
Map<String, CommandHandler> commands = new HashMap<>();
commands.put("close", Commands::close);
commands.put("toggle", Commands::toggle);
List<String> tokens = Arrays.asList(input.toLowerCase().split("\\s+"));
process(tokens, commands);
}
看起来不错。它又短又甜。它将输入字符串拆分为标记,但这就是它所做的全部工作。其余的则推迟到第二个 process()
方法。我们现在就这样写:
public static void process(List<String> tokens, Map<String, CommandHandler> commands) {
String command = tokens.get(0);
List<String> arguments = tokens.subList(1, tokens.size());
CommandHandler handler = commands.get(command);
if (handler != null) {
handler.handle(arguments)
}
}
这是命令解析逻辑的核心。它在映射中查找命令,如果找到则执行相应的处理程序。好处是这个方法不知道任何特定命令的任何信息。这一切都非常通用。
它还设置为支持子命令。注意它是如何获取 token 列表的吗?它如何将参数保存在单独的子列表中?这样做意味着它不仅可以为顶级命令调用,还可以为“render”等子命令调用。
最后一个难题是定义每个命令处理程序。我已将它们放入自己的类中,但您不必这样做。这些都可以是你原来的类中的方法(我只是不知道你命名它是什么)。
public class Commands {
public static void close(List<String> arguments) {
run = false;
}
public static void toggle(List<String> arguments) {
if (arguments.length == 0) {
System.err.println(notEnoughInfo);
return;
}
Map<String, CommandHandler> subCommands = new HashMap<>();
subCommands.put("render", arguments -> {
render = !render;
System.out.println("Render setting set to " + render);
});
subCommands.put("physics", arguments -> {
updatePhysics = !updatePhysics;
System.out.println("Physics update setting set to " + updatePhysics);
});
subCommands.put("trails", arguments -> {
showTrails = !showTrails;
System.out.println("Show trails setting set to " + showTrails);
});
subCommands.put("constellations", arguments -> {
showConstellations = !showConstellations;
System.out.println("Show constellations setting set to " + showConstellations);
});
process(arguments, subCommands);
}
}
toggle()
显示子命令解析。就像上面的代码一样,它创建子命令的映射并注册它们的名称和处理程序。就像上面一样,它调用与之前相同的 process()
函数。
这一次,由于处理程序都非常简单,因此没有必要将它们分解为单独的命名函数。我们可以使用匿名 lambda 来内联注册处理程序。就像 Commands::close
之前所做的那样,arguments -> { code }
创建一个 CommandHandler
内联。
关于java - 组织命令行界面命令的好方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51201059/
我是一家小型非营利组织的技术总监,我们正在制作一个新网站。我们提出了几个不同主页设计的模型,需要接收董事会成员的意见。是否有在线应用程序/程序/框架可以接收和组织用户评论?我正在寻找允许在查看页面时发
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
我阅读了很多关于 java 接口(interface)的文章。我知道你可以实现多态性和其他伟大的东西(函数指针等)。我有理论知识,但有一点点或什至没有。我一直在使用很多已经制作好的界面,比如“Runn
已关闭。此问题旨在寻求有关书籍、工具、软件库等的建议。不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以
我正在尝试在 Windows 7 上编写一个 python 脚本来与我的 Wacom Bamboo Pen 数位板交互。 Wacom 建议使用 WinTab API,它工作正常,但不适用于我的应用程序
我正在研究一种远程访问有关 Linux 服务器统计信息的方法,即 conky会显示。 有没有办法与 conky 交互并获取它显示的统计信息?如果没有,您能否推荐一种获取此类统计数据的好方法? (C/C
将所有这些代码包装在 UI: do { } block 中的原因是什么?我在哪里可以获得关于它的明确说明? UI: do { backgroundButton.setImage(UIImage
我需要将 R 连接到某些 C# 应用程序。我安装了 rscproxy_1.3 和 R_Scilab_DCOM3.0-1B5 添加了对 STATCONNECTORCLNTLib、StatConnecto
我正在尝试遍历接口(interface)片段以通过 id 查找我的特定结构并更改属性。 type A struct { ID ID Steps []Step } type Ste
我有两种不同格式的相同界面,一种是键由低破折号分隔的 JSON 格式,另一种是 javascript camelCase 格式: JSON 格式: interface MyJsonInterface
操作系统:Linux。 我正在尝试寻找可能的方法来为我的嵌入式系统实现 Web 界面。 目前有一个外壳(基于文本)和一小组命令用于查询设备。 我是网络开发新手,我的问题是: 我必须使用什么网络服务器?
我正在尝试运行基于 Mechanical Turk Qualtrics 的调查,并且需要一些似乎可以通过 Mechanical Turk API 使用的功能,例如 custom Qualificati
我见过漂亮的 MetroTwit 界面 http://www.metrotwit.com/ ;我想知道使用了哪些 WPF 组件来尝试重现它。 最佳答案 MetroTwit 设计师在这里:) 所有的控件
我有一个小问题。我需要将 Hadoop Web 界面与我们的 Web 应用程序集成。我只需要一个 Hadoop 接口(interface),我们可以在其中运行一些 hadoop 命令,例如 1
假设我有这个 JavaScript 函数: function updateMainBuff(buff) { // do some stuff } 我的
我试图找出一个窗口是否属于当前的虚拟桌面。 我从 winapi 中找到了 VirtualDesktopManager 类,但即使根据 here 它应该位于“shobjidl.h” header 中,当
关闭。这个问题是opinion-based .它目前不接受答案。 想改进这个问题?更新问题,以便 editing this post 提供事实和引用来回答它. 8年前关闭。 Improve this
我的 Tkinter GUI 界面有问题。文本不会在 shell 中打印,但如果将 Entry 放置在第一个 tk 窗口 get() 中,它就可以工作...帮助我吗? 这是我的代码: import o
我有一个在 MATLAB 中实现的随机微分方程组。只有 4 个变量与 Euler-Maruyama 集成,所以没有什么太花哨的......不过,技术细节对于这个问题并不重要。 您建议我如何构建一个 W
对于我创建的插件,我想添加一个网络界面,您可以从中更改设置并执行一些简单的数据操作。此数据操作应通过 sqlquerys 完成。我正在使用 sqlite 数据库,这就是问题开始的地方: 网站(serv
我是一名优秀的程序员,十分优秀!