我在Scala中创建了一个控制台界面应用程序作为todo-list。我的数据访问层使用Slick 3,接口使用简单的StdIn方法。但是我在读台词方面有一些困难。我的主菜单运行得很好,而内部菜单有时表现得很奇怪。特别是,当我第一次输入命令时,我没有得到任何结果,只是再次显示相同的菜单。然后,我输入任何命令,就会得到结果。如果我尝试输入一些3d时间的命令,我的程序就会以System.exit停止。下面是我的接口的代码:
object UserInterface {
def displayMainMenu(): Unit ={
println("Main menu:" + " \n1 - Login" + "\n2 - Exit")
println("\nChoose the operation you want to perform:")
val inputMainMenu = readInt()
buildMainMenu(inputMainMenu)
}
def buildMainMenu(inputNumber: Int) = inputNumber match {
case 1 => enterSystem()
case 2 => System.exit(0)
case _ => println("Your input was wrong. Try again"); displayMainMenu()
}
def enterSystem(): Unit ={
println("Input you login, please:")
val inputLogin = readLine()
println("Input you password, please:")
val inputPassword = readLine()
val checkLogin = Await.result(DAO.checkUserLogin(inputLogin, inputPassword), Duration.Inf).toString
val userId = DAO.selectUserId(inputLogin)
def changeOutputs(checkLogin: String):Unit = checkLogin match {
case "true" => println("You have successfully entered"); displayInnerMenu(); buildMenu(userId)
case "false" => println("Your input for login or password is wrong. Please, try again"); displayMainMenu()
case _ => println("Your input is wrong"); displayMainMenu()
}
changeOutputs(checkLogin)
}
def buildMenu(userId: Long): Unit ={
def chooseOption(number: Int):Unit = number match {
case 1 => displayFinishedTasks(userId)
case 2 => displayUnfinishedTasks(userId)
case 3 => addTask(userId)
case 4 => deleteTask()
case 5 => markTaskAsFinished(userId)
case 6 => displayMainMenu()
case _ => println("Your input is wrong"); displayMainMenu()
}
val inputNum = displayInnerMenu()
chooseOption(inputNum)
}
def displayInnerMenu():Int ={
println("TODO List:" + "\n1 - Display finished tasks" + "\n2 - Display unfinished tasks"
+ "\n3 - Add task" + "\n4 - Delete task" + "\n5 - Mark task as finished" + "\n6 - Get back to the main menu")
println("\nChoose the operation you want to perform:")
val inputNum = readInt()
inputNum
}
def displayAllTasks(id: Long) = {
println()
println("User's tasks:\n" + Await.result(DAO.selectTasksByUser(id), Duration.Inf).toList.toString)
displayInnerMenu()
}
def displayFinishedTasks(id: Long) = {
println()
println("User's finished tasks:\n" + Await.result(DAO.selectFinishedTasks(id), Duration.Inf).toList.toString)
displayInnerMenu()
}
def displayUnfinishedTasks(id: Long) = {
println()
println("User's unfinished tasks:\n" + Await.result(DAO.selectUnfinishedTasks(id), Duration.Inf).toList.toString)
displayInnerMenu()
}
def addTask(id: Long) = {
println()
println("Input the task name you want to create, please:")
val taskName = readLine()
Await.result(DAO.addTask(taskName, id), Duration.Inf)
displayInnerMenu()
}
def deleteTask() = {
println()
println("Choose the task you want to delete, please:")
val taskId = readLong()
Await.result(DAO.deleteTask(Some(taskId)), Duration.Inf)
displayInnerMenu()
}
def markTaskAsFinished(id: Long) = {
println()
println("Choose the task you want to mark as finished, please:")
val taskId = readLong()
Await.result(DAO.finishTask(Some(taskId), id), Duration.Inf)
displayInnerMenu()
}
}我想要的是某种无限循环,这样我就可以根据需要多次执行命令,或者设置限制。那么我可以在这段代码中引入哪些更改呢?如果能得到一些帮助,我将不胜感激!
发布于 2017-11-27 14:19:33
您的特殊问题似乎来自于这样一个事实:enterSystem中的changeOutputs调用displayInnerMenu,后者从输入读取Int,但对它没有任何有用的作用。也许您应该在调用displayInnerMenu的大多数地方调用buildMenu。
而且,你似乎应该提高你的调试技能。这是一项至关重要的技能,这段代码并不难调试。
从更广泛的角度来看,这是一个复杂的主题,没有简单的最佳答案。但肯定有一些不好的,不幸的是,你的就是其中之一。我最不喜欢的是你的代码中菜单项标题和菜单项动作之间的大分隔。(想象一下在中间添加新的菜单项需要什么。或者需要什么才能创建更深层次的菜单,其中一些项目在不同级别之间共享。)所以我会重写大部分代码。作为一个面向对象的人而不是一个FP的人,我会这样做:
object UserInterface {
// should be non-generic for simplicity of the rest of the code
trait MenuAndStateNG {
def runMenu(): MenuAndStateNG
}
trait MenuItem[S] {
val name: String
def doAction(state: S, curMenu: MenuAndStateNG): MenuAndStateNG
}
case class Menu[S](header: String, items: Seq[MenuItem[S]]) {}
case class MenuAndState[S](menu: Menu[S], state: S) extends MenuAndStateNG {
def runMenu(): MenuAndStateNG = {
var inputNum: Int = -1
var isFirstRun = true
// we use 1-based indices in the menu
while (inputNum <= 0 || inputNum > menu.items.length) {
if (!isFirstRun) {
println("Your input was wrong. Try again")
}
isFirstRun = false
println(menu.header + ":")
println(menu.items.zipWithIndex.map({ case (item, index) => s"${index + 1} - ${item.name}" }).mkString("\n"))
println("Choose the operation you want to perform:")
inputNum = StdIn.readInt()
}
println()
val nextMenu = menu.items(inputNum - 1).doAction(state, this)
nextMenu
}
}
// most of menu items doesn't change current menu
// let's make it easier to implement
trait SimpleMenuItem[S] extends MenuItem[S] {
override def doAction(state: S, curMenu: MenuAndStateNG): MenuAndStateNG = {
doSimpleAction(state)
curMenu
}
def doSimpleAction(state: S): Unit
}
def start(): Unit = {
var curMenu: MenuAndStateNG = MenuAndState(mainMenu, ())
var isFirstRun = true
while (true) {
if (!isFirstRun) {
println
}
isFirstRun = false
curMenu = curMenu.runMenu()
}
}
private val loginItem = new MenuItem[Unit] {
override val name = "Login"
override def doAction(state: Unit, curMenu: MenuAndStateNG): MenuAndStateNG = {
println("Input you login, please:")
val inputLogin = StdIn.readLine()
println("Input you password, please:")
val inputPassword = StdIn.readLine()
val checkLogin = Await.result(DAO.checkUserLogin(inputLogin, inputPassword), Duration.Inf).toString
val userId = DAO.selectUserId(inputLogin)
checkLogin match {
case "true" =>
println("You have successfully entered")
MenuAndState(userMenu, userId)
case "false" =>
println("Your input for login or password is wrong. Please, try again")
curMenu
case _ =>
println("Your input is wrong")
curMenu
}
}
}
private val exitItem = new MenuItem[Unit] {
override val name = "Exit"
override def doAction(state: Unit, curMenu: MenuAndStateNG): MenuAndStateNG = {
System.exit(0)
null // null is bad but it doesn't matter by now
}
}
private val displayFinishedTasks = new SimpleMenuItem[Int] {
override val name: String = "Display finished tasks"
override def doSimpleAction(state: Int): Unit = {
println("User's finished tasks:\n" + Await.result(DAO.selectFinishedTasks(id), Duration.Inf).toList.toString)
}
}
private val displayUnfinishedTasks = new SimpleMenuItem[Int] {
override val name: String = "Display unfinished tasks"
override def doSimpleAction(state: Int): Unit = {
println("User's unfinished tasks:\n" + Await.result(DAO.selectUnfinishedTasks(id), Duration.Inf).toList.toString)
}
}
private val displayAllTasks = new SimpleMenuItem[Int] {
override val name: String = "Display all tasks"
override def doSimpleAction(state: Int): Unit = {
println("User's tasks:\n" + Await.result(DAO.selectTasksByUser(id), Duration.Inf).toList.toString)
}
}
private val addTask = new SimpleMenuItem[Int] {
override val name: String = "Add task"
override def doSimpleAction(state: Int): Unit = {
println("Input the task name you want to create, please:")
val taskName = readLine()
Await.result(DAO.addTask(taskName, id), Duration.Inf)
}
}
private val deleteTask = new SimpleMenuItem[Int] {
override val name: String = "Delete task"
override def doSimpleAction(state: Int): Unit = {
println("Choose the task you want to delete, please:")
val taskId = readLong()
Await.result(DAO.deleteTask(Some(taskId)), Duration.Inf)
}
}
private val markTaskFinished = new SimpleMenuItem[Int] {
override val name: String = "Mark task as finished"
override def doSimpleAction(state: Int): Unit = {
println("Choose the task you want to mark as finished, please:")
val taskId = readLong()
Await.result(DAO.finishTask(Some(taskId), id), Duration.Inf)
}
}
private val logoutTask = new MenuItem[Int] {
override val name = "Get back to the main menu"
override def doAction(state: Int, curMenu: MenuAndStateNG): MenuAndState[Unit] = {
MenuAndState(mainMenu, ())
}
}
val mainMenu: Menu[Unit] = Menu("Main menu", List(loginItem, exitItem))
val userMenu: Menu[Int] = Menu("User menu", List(
displayAllTasks,
displayFinishedTasks,
displayUnfinishedTasks,
addTask,
deleteTask,
markTaskFinished,
logoutTask))
}主要思路如下:
从外部选择下一个“菜单状态”(MenuAndState)
MenuAndState看起来像MenuAndStateNG --也就是说,可以运行它来获取下一个MenuAndStateNG。从内部来看,它被分成一个“固定部分”(Menu) =标题+项目列表和“可变部分”= state。通过引入这种分离,我能够使菜单项的def
userMenu实际上成为常量,菜单项不会改变菜单,而只是返回到其父菜单。为了简化此场景的代码,将curMenu作为参数传递给doAction,并且有一个SimpleMenuItem始终返回它\给出这样的设计,你所需要的就是:
val mainMenu和userMenu val作为这些菜单项的列表H139从mainMenu开始运行无限循环(在code中完成
注意,因为MenuAndStateNG从它的runMenu返回下一个MenuAndStateNG,所以我可以使用无限循环,而不是在每次菜单迭代时加深堆栈(这通常是一个坏主意)。
https://stackoverflow.com/questions/47502680
复制相似问题