키오스크 기능 구현
저번 포스팅에 이어서 클래스를 나누는 기준과 분업에 대한 기준에 대해 처음에는 어려움이 많아서 많은 고민을 했던것과 그 과정에 대해서 써보려고 한다. 일단 나눈 클래스와 클래스를 기반으로 메뉴판을 띄우는 화면이다.
클래스의 기준은 일단 공차 홈페이지로 가서 실제 메뉴들을 보면서 나누어보았다. 음료 종류의 카테고리를 기준으로 나누었고 메뉴판에 상세 설명을 보면 알 수 있듯이 각가의 메뉴의 타이틀을 클래스로 구분한것을 볼 수 있다. 그리고 이 메뉴들의 클래스를 살펴 보자
•베스트 콤비네이션의 코드
class BestComMenu {
private val menuList = ArrayList<MenuItem>()
init {
menuList.add(MenuItem("블랙밀크티+펄 ",4700))
menuList.add(MenuItem("타로밀크티+펄 ",4700))
menuList.add(MenuItem("우롱티+코코넛+밀크폼",4700))
menuList.add(MenuItem("아메리카노+펄 ",4000))
}
fun displayMenu(){
println("\n[ Best Combination MENU ]")
for((index, item)in menuList.withIndex()){
println("${index + 1}. ${item.name} |${item.price} 원|")
}
println("0. 뒤로가기")
}
fun getMenuItem(index: Int): MenuItem?{
return if(index in 1..menuList.size) menuList[index - 1] else null
}
}
이런식으로 큰 메뉴 안에 세세한 메뉴들을은 init으로 클래스를 인스턴스화 할때 가장 먼저 실행되는 부분이다. ArrayList로 구현하여 MenuItem에 담기게 된다. 그리고 메뉴 리스트에 있는걸 보여주는 displayMenu 메소드, 사용자가 고른 메뉴를 인덱스에서 따라 선택 항목을 검색할 수 있는 메소드를 만들어 주었다.
이렇게 노션에서 팀원들과 자료 조사를 통해서 클래스를 구현 할 수 있었다. 음 이걸 어플을 구현하기 이전에 한눈에 보기 쉽고 방향성을 어떻게 잡아야 할지 어플을 구현하기 이전에 와이어 프레임과 같은것 이라고 할 수 있을것 같다. 실제로 보여지는건 터미널에서만 보여지기 때문에 UI가 어떻고 이런걸 생각하는게 아닌것이다.
각각의 메뉴의 코드는 클래스명과 안에 메뉴 내용이 다른점이 있다. 그리고 데이터 클래스인 MenuItem을 살펴보자
•MenuItem Class
data class MenuItem (val name: String, val price:Int){
override fun toString(): String{
return "$name | ${price}원"
}
}
메뉴아이템 클래스는 데이터 클래스를 정의하며 자바와는 다르게 getter,setter을 따로 구현해주지 않아도 자동으로 생성자와 canonical methods까지 정의가 된다. 코틀린을 처음 접하면 형식이 너무 달라서 데이터 클래스라고 생각을 할 수 없겠지만 코틀린은 이러한 형식으로 데이터 클래스를 정의 할 수 있다.
다음으로는 주문의 기능이 있는 클래스이다.
•Order Class
class Order {
private val orderList = ArrayList<MenuItem>()
fun addToOrder(item: MenuItem){
orderList.add(item)
println("\"${item.name}\"이(가) 장바구니에 추가 되었습니다.")
}
fun displayOrder() {
if (orderList.isEmpty()) {
println("\n장바구니가 비어있습니다.")
println("====메뉴를 선택해주세요====")
} else {
println("[ | 장바구니 | ]")
for (item in orderList) {
println("${item.name} | ${item.price} 원 | ")
}
}
}
fun getTotalPrice(): Int{
return orderList.sumOf { it.price }
}
suspend fun clearOrder(){
orderList.clear()
println("\"주문이 완료되었습니다.\"")
delay(1000)
println("\"결제가 완료되었습니다.\"")
}
fun isEmpty(): Boolean {
return orderList.isEmpty()
}
}
MenuItem에 있는 데이터를 가져와서 장바구니의 기능을 하며 장바구니가 비어있다면 예외처리를 해놓은 메소드와 메뉴 리스트에 저장되어있는 메뉴에서 가격을 불러와서 장바구니의 담긴 총 금액을 보여주는 메소드, 주문이 완료돼서 리스트를 초기화하는 clear() 이 포함된 메소드도 있다. isEmpty 메소드는 장바구니가 비어있을때 사용하는 메소드이다.
다음으로 메인 클래스를 살펴보자
•MainClass
suspend fun main() {
val scanner = Scanner(System.`in`)
val coffeeMenu = CoffeeMenu()
val bestcomMenu = BestComMenu()
val jewelryMenu = JewelryMenu()
val milkTMenu = MilkTMenu()
val originalTMenu = OriginalTMenu()
val order = Order()
val payment = Payment(order)
val waiting = Waiting()
println("\n\"어서오세요. 공들여 맛있는 공차입니다.\"")
// println("\n현재 잔액을 입력해주세요 :")
// var cash = scanner.nextInt()
// val won = "원"
while (true) {
// println("\n\"어서오세요. 공들여 맛있는 공차입니다.\"")
println("\n아래 메뉴판을 보시고 메뉴를 골라 입력해주세요")
println("[ 공차 메뉴 ]")
println("1. 베스트 콤비네이션 | 공차 고객들이 즐겨찾는 티와 토핑의 환상의 조합")
println("2. 오리지널 티 | 찻잎을 정성껏 우려낸 프리미엄 잎차")
println("3. 밀크티 | 프리미엄 잎차에 부드러운 밀크를 넣은 공차의 No.1메뉴")
println("4. 쥬얼리 | 쥬얼리가 기본으로 들어간 맛있는 밀크티")
println("5. Coffee | 프리미엄 잎차와 100% 아라비카 원두의 이색 만남")
println("6. Order | 장바구니를 확인 후 주문합니다.")
println("7. Cancel | 진행중인 주문을 취소합니다.")
print("메뉴 선택:")
val choice = scanner.nextInt()
when (choice) {
1 -> {
bestcomMenu.displayMenu()
print("선택 ")
val bestcomChoice = scanner.nextInt()
if (bestcomChoice == 0) continue
val menuItem = bestcomMenu.getMenuItem(bestcomChoice)
if (menuItem != null) {
order.addToOrder(menuItem)
} else if (bestcomChoice != 0) {
println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
2 -> {
originalTMenu.displayMenu()
print("선택 ")
val originalTChoice = scanner.nextInt()
if (originalTChoice == 0) continue
val menuItem = originalTMenu.getMenuItem(originalTChoice)
if (menuItem != null) {
order.addToOrder(menuItem)
} else if (originalTChoice != 0) {
println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
3 -> {
milkTMenu.displayMenu()
print("선택 ")
val milkTChoice = scanner.nextInt()
if (milkTChoice == 0) continue
val menuItem = milkTMenu.getMenuItem(milkTChoice)
if (menuItem != null) {
order.addToOrder(menuItem)
} else if (milkTChoice != 0) {
println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
4 -> {
jewelryMenu.displayMenu()
print("선택 ")
val jewerlryChoice = scanner.nextInt()
if (jewerlryChoice == 0) continue
val menuItem = jewelryMenu.getMenuItem(jewerlryChoice)
if (menuItem != null) {
order.addToOrder(menuItem)
} else if (jewerlryChoice != 0) {
println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
5 -> {
coffeeMenu.displayMenu()
print("선택 ")
val coffeeChoice = scanner.nextInt()
if (coffeeChoice == 0) continue
val menuItem = coffeeMenu.getMenuItem(coffeeChoice)
if (menuItem != null) {
order.addToOrder(menuItem)
} else if (coffeeChoice != 0) {
println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
6 -> {
if(order.isEmpty()){
println("장바구니가 비어있습니다.")
println("=====메뉴를 골라주세요")
continue
}
order.displayOrder()
print("결제금액 :")
println("${order.getTotalPrice()}원" )
println("1. 주문 2. 메뉴추가")
print("주문하려면 1번 다른 메뉴를 보고 싶으면 2번을 눌러주세요: ")
val orderChoice = scanner.nextInt()
delay(1000)
when (orderChoice) {
1 -> {
payment.startPayment()
delay(2000)
waiting.recordCompletedPayment(payment.getPaymentDetails())
waiting.printWaitingNumber(false)
waiting.printReceiptCount()
order.clearOrder()
}
2 -> continue
else -> println("잘못된 번호를 입력했어요. 다시 입력해주세요")
}
}
7 -> {
// 취소를 누르면 메뉴판으로 돌아가기
// order.clearOrder()
println("진행중인 주문이 취소되었어요.")
continue
}
}
}
}
이런식으로 when문을 사용하여 메뉴판에 번호가 입력 됐을때 수행하는걸 구현 하였다. 모든 메뉴 리스트를 클래스로 구현해두고 메소드로 하여금 불러만 오면되는것으로 구현을 하였다.
메뉴판에서 메뉴를 고르면 실행되는 결과창이다. 이렇게 각 리스트에 저장된 메뉴와 가격이 잘 출력되는것을 볼 수 있다. 주문 항목을 누르면 담았던 메뉴와 가격이 출력되고 결제 금액도 출력되는것을 볼 수 있다.
추가기능
공차를 이용하다보면 펄추가와 같이 추가 토핑 부분이 있다는걸 알 수 있다. 이 부분은 팀원분이 구현을 해주셨는데 어떻게 적용해야할지 감이 안잡혀서 고민중에 있다. 음료수와 같은 메뉴로 구성이 되었는데 어떻게 이를 연결하고 각 음료의 종류를 선택 하였을때 추가 토핑을 하시겠습니까의 형식으로 해야할것 같은데 고민중에 있다. 금요일까지 제출해야하기 때문에 예외처리와 구현 레벨 향상을 목표로 열심히 해야겠다.
