토요일에 푹 쉬고 일요일이 되어서 과제를 다시 켰다.
기본 구현은 다행히 거의 다 왔다.
장바구니의 합계는 다음과 같은 문제를 겪는 중이다.
아래와 같이 주문하시겠습니까?
마라탕 | 0단계 | 안맵습니다. | 8000 |
꿔바로우 | 미니 | 저렴합니다 | 7000 |
마라탕 | 1단계 | 신라면정도. | 8000 |
[8000, 8000, 7000, 8000, 7000, 8000]
1. 확인 2. 메뉴판
장바구니에 담을 때
전에 담겨 있던 가격이 자꾸 중복 추가가 된다.
왜 자꾸 바가지를 씌우나 궁금해서 합계를 안 구하고 어떻게 담기는지 출력해봤다.
문제의 그 코드
public void setTotalList(){
int total = 0;
for(int n = 0 ; n+6 <= shoppingBasket.size(); n += 6){
int price = Integer.parseInt(shoppingBasket.get(n + 4));
totalList.add(price);
}
System.out.println(totalList);
}
그런데 키오스크 메인 함수 말고, 별도의 메인 함수를 만들어 테스트하면 잘 나온다.
아래와 같이 주문하시겠습니까?
마라탕 | 1단계 | 신라면정도. | 8000 |
마라탕 | 1단계 | 신라면정도. | 8000 |
음료 | 물 | 삼다수입니다. | 500 |
[8000, 8000, 500]
이건 메인 키오스크의 로직에 문제가 있다는 뜻인 것 같다.
그림을 그려보며 생각을 정리해봤다.
문제의 원인은 다음과 같다.
! totalList가 지역 변수이다.
합계를 구하는 저 메서드는 배열을 조회해 n+4의 값을 리스트에 추가한다.
그런데 totalList가 지역 변수로 선언되어 있으면 이전에 조회한 값은 그대로 저장되고, 다음에 조회한 값이 순차적으로 다시 추가되어 중복될 수밖에 없다.
별도의 테스트 메인 함수의 경우, 반복문 없이 장바구니에 요소를 추가한 후 해당 메서드를 사용해 조회가 1번밖에 일어나지 않았다.
하지만 메인 키오스크 로직은 계속해서 다음 행동을 선택할 수 있도록 반복문을 사용했기 때문에 조회할 때마다 리스트의 값이 늘어난다.
한 번 바꿔보자.
아래와 같이 주문하시겠습니까?
마라탕 | 0단계 | 안맵습니다. | 8000 |
마라탕 | 2단계 | 얼얼합니다. | 8000 |
꿔바로우 | 미니 | 저렴합니다 | 7000 |
[8000, 8000, 7000]
1. 확인 2. 메뉴판
다행이다. 간단하게 바뀐다.
lv와 iv의 역할이 얼마나 다른지, 반복문의 위치가 얼마나 중요한지 배우고 있다...
남은 할 일
1. 문제 해결하기
마라탕을 선택하면 이렇게 메뉴판을 보여준다.아래 음식을 보고 번호를 골라 입력해 주세요.
0. 0단계 | 안맵습니다. | 8000 |
1. 1단계 | 신라면정도. | 8000 |
2. 2단계 | 얼얼합니다. | 8000 |
그럼 여기서 이상한 수나 글자를 입력하면 다시 입력해달라고 해야 하는데 0~8까지 실제로 번호가 매겨져있어서, 3을 누르면 사천꿔바로우 주문이 된다. 제한을 걸어줘야 한다.
2. 주문하기 기능 구현
장바구니에서 1. 확인 을 눌렀을 때 주문 번호를 발급해주고, 3초 후 다시 메인 주문 화면으로 돌아가야 한다.
일단 1번 문제부터 해결하자.
add 메서드와 showeach를 수정해야 한다.
근데 저것 자체를 건드리기보다는 저 두 함수를 감싸서 제한을 걸어주는 게 좋을 것 같다.
아니지, show에서 제한을 걸어주면 뒤는 안 건드려도 된다.
case "마라탕", "꿔바로우", "음료":
System.out.println("아래 음식을 보고 번호를 골라 입력해 주세요.");
// 카테고리 출력
b.printProduct(category);
// 음식 선택
int choice = sc.nextInt();
b.showEachShoppingBasket(choice);
System.out.println("위 메뉴를 장바구니에 추가하시겠습니까?");
System.out.print("1. 확인 ");
System.out.println("2. 취소");
// 옵션 선택
int choice2 = sc.nextInt();
switch (choice2){
case 1:
b.addShoppingBasket(choice);
System.out.println("메뉴가 무사히 장바구니에 담겼습니다.");
System.out.println("결제를 진행하시려면 [1. 장바구니 확인 및 주문]을 선택해 주세요.");
System.out.println();
break;
while문으로 반복 조건을 걸어주면 된다.
지금 어떻게 하면 choice로 true가 나오는지 고민중이다.
그런데 문제가 하나 더 있다.
스캐너가 int 타입이기 때문에 문자를 입력하면 전체가 종료되어 버린다.
우선 이 예외 문제를 해결하고, 전체 스캐너를 다시 string 타입으로 바꾼 후 형변환 해주자.
if(Objects.equals(category,"마라탕")){
while (choice<0 || choice >2){
System.out.println("잘못된 번호를 입력하셨습니다. 다시 입력해 주세요.");
choice = sc.nextInt();
}
b.showEachShoppingBasket(choice);
System.out.println("위 메뉴를 장바구니에 추가하시겠습니까?");
System.out.print("1. 확인 ");
System.out.println("2. 취소");
}
아직 !를 잘 쓰지 못하는 것 같다.
일단 이 형태로 마무리.
주문 번호 발급과 3초 후 메인 화면으로 돌아가기 기능을 구현하고 있다.
TimeTask 사용 방법을 정리해본다.
Timer 클래스의 메서드는 보통 task를 인자로 받는데, 이 task는 메서드를 담은 객체(=클래스)의 형태여야 한다.
그리고 그 클래스는 TimeTask를 상속해야 한다.
1. Timer 클래스의 메서드는 시간 지연, 지정과 관련되어 있다. 일정 시간 후, 혹은 일정 시간에 시현하고 싶은 task를 담은 클래스를 생성한다.
2. task 클래스에 TimeTask를 상속시키고, run 함수를 override한다.
그 안에 내용을 작성한다.
3. main 함수로 돌아가 Timer 객체를 선언하고, task 객체를 선언한다.
4. 그 다음 생성자 형식에 맞춰 사용한다.
잘 되는지 해보자.
메인 스레드가 먼저 실행된다. 메뉴판 호출이 먼저 되고, 3초 후에 task 클래스가 실행된다. 그렇게 하면 의도랑 전혀 다르지.
동기화 문제는 해결이 어렵다! 포기
Thread.sleep을 사용해 보았다.
try-catch 구문 안에서 사용해야 한다고 한다.
주문 번호가 계속 초기화된다.
ordernumber를 루프문 바깥으로 아예 꺼내버렸다.
루프문에 대한 이해는 해도해도 모자라다.
이제 각 메뉴들에 디테일을 추가해주자.
- 장바구니를 확인했을 때 비어있을 경우에 대한 정의가 되지 않음.
public void getShoppingBasket(){
// 쇼핑리스트의 0(2)2(1)3(1)4...(묶음) 가져오기
// (n, n+2, n+3, n+4),(n+6,n+8,n+9,n+10)...
//매개 변수 n를 주고, n을 6씩 증가시키고, 값을 가져올 수 없을 때?? shoppingBasket의 길이가 n+6보다 작을 때까지 반복
//Key 값으로 지정한 것이 아니기 때문에, Menu에 새로운 요소가 추가되면 여기 함수도 수정해야 한다는 약점이 있음.
if(shoppingBasket.isEmpty()){
System.out.println("장바구니가 비어 있습니다.");
}else{
System.out.println("아래와 같이 주문하시겠습니까?");
for (int n = 0 ; n+6 <= shoppingBasket.size(); n += 6) {
System.out.print(shoppingBasket.get(n) + " | ");
System.out.print(shoppingBasket.get(n + 2) + " | ");
System.out.print(shoppingBasket.get(n + 3) + " | ");
System.out.println(shoppingBasket.get(n + 4)+ " | ");
}
setTotalList();
System.out.print("1. 확인 ");
System.out.println("2. 메뉴판");
}
}
정확한 연결점이 없다.
2. 메뉴판이 실행되는 걸 보니!! finalChoice 스캐너를 받는다.
get 메서드를 분리하자.
if구문을 삽입하고, break를 쓸 수 있도록 했다.
get 메서드는 핵심 구현부만 남기고 if 구문을 다 지웠다.
switch (category) {
case "1":
if(b.shoppingBasket.isEmpty()){
System.out.println("장바구니가 비어 있습니다.");
System.out.println("3초 후 메뉴판으로 돌아갑니다.");
try {
// 3초 동안 현재 스레드를 일시 정지
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("시스템 오류");
}
break;
}else {
b.getShoppingBasket();
}
int finalChoice = sc.nextInt();
이제 int 스캐너들을 전부 string으로 바꿔주고, 그에 따른 작업을 하면 기초는 끝난다. 넘 아쉽게도... 내일 마저 해야겠다.
너무너무 머릿속에 안 들어와서 아예 논외로 두었던 인터페이스, 람다식, try catch, 자료구조형을 이렇게 만나니 조금 익숙해지는 느낌이다.
역시 나에겐 반복 학습이 부족하다.
그래도 기특하다.
주말에도 6시간이나 코드를 짜다니! 굿
내일 화이팅!
(왜 자꾸 출간이 안되어있지? ㅠ)