주말동안 개인프로젝트를 해볼까한다. 주제는 키오스크 구현해보기.
★ 요구사항
- 메뉴 클래스 만들기 ( 이름, 설명 필드 포함)
- 상품 클래스 만들기 ( 이름, 가격, 설명 필드 포함)
- 상품 클래스의 이름, 설명 필드는 메뉴 클래스를 상속받아 사용하는 구조로 개발
- 주문 클래스도 만들어서 상품 객체를 담을 수 있도록 하기
시작해보자.
일단 큰크림을 그려보자면
전체 메뉴판을 만들자.
1~4번까지는 상위 메뉴들, 5번은 주문(장바구니포함) , 6번은 취소 메뉴로 하고
1~4번을 선택하면 각각 상세 메뉴판이 나온다. 메뉴 선택 후에 다시 메뉴판으로 돌아온다.
class MenuItem {
private String name;
private double price;
public MenuItem(String name, double price) {
this.name = name;
this.price = price;
}
먼저 메뉴 항목을 나타내는 MenuItem 클래스를 만들어 준다. name과 price 변수를 private로 선언한 이유는 외부에서 직접 접근하지 못하게 하기 위해서이다. 클래스 내부에서만 변수에 접근하여 조작 가능하도록 만든다. 이를 캡슐화라고 한다.
그리고 this를 사용한 이유는 생성자의 매개변수 name과 price를 클래스의 name과 price 멤버 변수에 할당하기 위해서이다. 이렇게 하면 클래스의 멤버 변수와 매개변수를 구분할 수 있다.
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
이후 게터 메소드를 통해 private로 선언되어 있는 멤버 변수 name과 price에 접근한다. 게터 메소드를 쓴 이유는 멤버 변수의 값을 보호할 수 있고 만약 멤버 변수의 이름이나 데이터 형식을 변경해야 한다면, 게터 메소드만 수정하면 되기 때문에 편리하다.
class Burger extends MenuItem {
public Burger(String name, double price) {
super(name, price);
}
}
버거 항목을 나타내는 Burger 클래스 정의를 해준다. extends를 써서 MenuItem 클래스를 상속받아 확장해준다. super를 써서 부모 클래스에서 name과 price 멤버 변수를 호출해준다. 상속을 쓰는 이유는 부모 클래스의 멤버 변수와 메소드를 받을 수 있기 때문에 코드 재사용을 피할 수 있다.
이후 FrozenCustard, Drink, Beer 클래스도 똑같이 해준다.
class OrderSystem {
private ArrayList<MenuItem> cart = new ArrayList<>();
private Scanner scanner = new Scanner(System.in);
주문 시스템을 관리하는 OrderSystem 클래스를 정의한다. 그리고 ArrayList를 사용한다. ArrayList를 사용한 이유는 크기 조절이 가능한 자료 구조이기 때문이다. 이는 주문 항목의 수량이나 종류에제한을 두지 않고 자유롭게 관리할 수 있도록 한다.
또한 Scanner을 사용해 입력값을 넣는 방식으로 사용하게끔 했다.
public void displayMenu() {
System.out.println("SHAKESHACK BURGER에 오신걸 환영합니다.");
System.out.println("[ SHAKESHACK MENU ]");
System.out.println();
System.out.println("1. Burgers | 앵거스 비프 통살을 다져만든 버거");
System.out.println("2. Frozen Custard | 매장에서 신선하게 만드는 아이스크림");
System.out.println("3. Drinks | 매장에서 직접 만드는 음료");
System.out.println("4. Beer | 뉴욕 브루클린 브루어리에서 양조한 맥주");
System.out.println();
System.out.println("[ ORDER MENU ]");
System.out.println("5. Order | 장바구니를 확인 후 주문합니다.");
System.out.println("6. Cancel | 진행중인 주문을 취소합니다.");
System.out.println();
System.out.print("메뉴를 선택해주세요: ");
}
displayMenu 메소드를 만들어서 메뉴를 볼 수 있게 하였다.
displayBurgerMenu, displayFrozenCustardMenu, displayDrinksMenu, displayBeerMenu도 각각 만들어 주었다.
public void orderBurgers() {
displayBurgerMenu();
int burgerChoice = scanner.nextInt();
버거 주문을 처리하는 oderBurgers 메소드를 만들고 displayBurgerMenu를 호출해준다. 사용자가 콘솔에서 문자열을 입력하면 nextInt는 그 값을 정수로 변환해 준다. 그리고 burgerChoice에 변수로 할당해준다. 컴퓨터는 우리말을 모르기 때문에 이렇게 여러 과정을 거쳐서 이해한다.
if (burgerChoice >= 1 && burgerChoice <= 5) {
Burger[] burgers = {
new Burger("ShackBurger", 6.9),
new Burger("SmokeShack", 8.9),
new Burger("Shroom Burger", 9.4),
new Burger("Cheeseburger", 6.9),
new Burger("Hamburger", 5.4)
};
if문을 통해 사용자가 선택한 버거 항목이 유효한 범위인지 확인한다. 각 버거 항목에 대한 객체를 생성하고 배열에 저장할 수 있게 한다.
System.out.println(burgers[burgerChoice - 1].getName() + "를 장바구니에 추가하시겠습니까?");
System.out.println("1. 확인 2. 취소");
int confirmChoice = scanner.nextInt();
사용자가 선택한 버거 항목을 출력하고 추가 여부를 묻고, 확인 또는 취소 옵션을 제공하고 입력 받는다. 버거 항목을 불러올때 -1을 한 이유는 인덱스는 0부터 시작하기 때문이다. 사용자가 인식하는 메뉴 번호는 1~5이고 실제 배열에 저장된 인덱스 값은 0~4이므로 -1을 해서 사용자와 프로그램 간의 번호 매칭을 올바르게 해준다.
if (confirmChoice == 1) {
cart.add(burgers[burgerChoice - 1]);
System.out.println(burgers[burgerChoice - 1].getName() + "를 장바구니에 추가했습니다.");
} else {
System.out.println(burgers[burgerChoice - 1].getName() + "를 장바구니에 추가하지 않았습니다.");
}
} else {
System.out.println("잘못된 메뉴 선택입니다. 다시 선택해주세요.");
}
사용자가 확인(1)을 선택하면 해당 버거 항목을 장바구니에 추가하고 메시지를 출력한다. 취소(2)를 선택하면 해당 버거 항목을 장바구니에 추가하지 않았음을 메시지로 알린다. 그 외의 값을 입력하면 잘못된 메뉴 메시지가 나온다.
orderFrozenCustard, orderDrinks, orderBeer 메소드도 같은 형식으로 작성해준다.
public void placeOrder(int orderNumber) {
if (cart.isEmpty()) {
System.out.println("장바구니가 비어 있습니다. 주문할 메뉴를 추가해주세요.");
} else {
double total = 0;
System.out.println("아래와 같이 주문 하시겠습니까?");
System.out.println();
System.out.println("[ Orders ]");
주문을 처리하고 완료하는 placeOrder를 만들어 준다. if문에서 cart.isEmpty를 확인하여 장바구니가 비어 있는지 검사한다. 비어 있다면 메시지를 출력해준다. 그렇지 않으면 주문 내역 및 총 가격을 출력한다.
double total = 0;는 total이라는 이름의 double 데이터 타입의 변수를 선언하고 0으로 초기화 하기 위해 써준다.
for (MenuItem item : cart) {
System.out.println(item.getName() + " | W " + item.getPrice());
total += item.getPrice();
}
System.out.println();
System.out.println("[ Total ]");
System.out.println("W " + total);
System.out.println();
System.out.println("1. 주문 2. 메뉴판");
int orderConfirm = scanner.nextInt();
for-each를 써서 모든 주문 항목을 처리하고, 주문 총액을 계산하며, 사용자에게 주문 확인과 메뉴판 재선택 옵션을 제공할 수 있게 해준다. total += item.getPrice();를 사용하여 현재까지의 주문 총액을 누적해서 계산한다.
if (orderConfirm == 1) {
System.out.println("주문이 완료되었습니다!");
System.out.println("대기번호는 [ " + orderNumber + " ] 번 입니다.");
사용자가 1을 선택하여 주문을 완료하면, 주문 완료 메시지와 대기 번호를 표시한다. orderNumber는 주문 번호를 추척하는 변수로 설정한다.
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
cart.clear();
} else if (orderConfirm == 2) {
return;
}
}
}
try-catch를 사용하여 3초 동안 대기한다. try-catch 블록은 예외 처리를 위해 사용된다. TimeUnit.SECONDS.sleep(3)은 현재 스레드(프로그램의 실행 흐름)를 3초동안 일시 중지한다는 의미이다. InterruptedException은 스레드가 중단되는 동안 발생할 수 있는 예외이다. 그리고 cart.clear()를 사용해서 3초 대기 후에 장바구니를 비운다. 이는 주문이 완료된 후, 장바구니를 초기화하여 새로운 주문을 받을 수 있게 한다.
public void cancelOrder() {
if (!cart.isEmpty()) {
System.out.println("진행하던 주문을 취소하시겠습니까?");
System.out.println();
System.out.println("1. 확인 2. 취소");
int cancelChoice = scanner.nextInt();
현재 주문을 취소하는 역할인 cancelOrder 매소드를 만든다. if문을 써서 장바구니가비어있지 않으면, 사용자에게 주문 취소 여부를 묻고 확인 또는 취소 옵션을 제공한다.
if (cancelChoice == 1) {
cart.clear();
System.out.println("주문이 취소되었습니다.");
} else {
System.out.println("주문이 취소되지 않았습니다.");
}
} else {
System.out.println("취소할 주문이 없습니다.");
}
}
위와 같이 if문을 써서 코드를 설계했다.
public class Kiosk {
public static void main(String[] args) {
OrderSystem orderSystem = new OrderSystem();
Scanner scanner = new Scanner(System.in);
int orderNumber = 0;
while (true) {
orderSystem.displayMenu();
int choice = scanner.nextInt();
switch (choice) {
case 1:
orderSystem.orderBurgers();
break;
case 2:
orderSystem.orderFrozenCustard();
break;
case 3:
orderSystem.orderDrinks();
break;
case 4:
orderSystem.orderBeer();
break;
case 5:
orderSystem.placeOrder(orderNumber);
orderNumber++;
break;
case 6:
orderSystem.cancelOrder();
break;
default:
System.out.println("잘못된 선택입니다. 다시 선택해주세요.");
}
}
}
}
Kiosk 클래스를 정의하고 프로그램 진입점인 main 메소드를 만든다. 주문 시스템을 나타내는 OrderSystem 클래스의 객체를 생산한다. 주문의 대기번호를 나타내는 변수 orderNumber를 초기화 해준다.
while(true)을 써서 프로그램을 종료하기 전까지 계속 실행되게 한다. orderSystem.displayMenu();를 써서 메뉴를 화면에 출력한다. 사용자로부터 정수 입력을 받아 choice변수에 저장한다. 저장된 choice 값을 기반으로 switch문을 작성한다.
사용자가 입력한 값에 따라 메소드가 호출된다. case 5의 orderNumber++는orderNumber가 증가하여 새로운 대기번호가 생성되도록 해준다.
switch문에서 이외의 선택을 한다면 문구가 출력되도록 default를 써준다.
-끝-
코드를 좀 더 깔끔하게 하고싶다. 머리 싸매고 구글링하고 책보면서 만든거라 두서가 없지만 나름 선방한 것 같다. 내일은 선택사항에 나와있는 기능을 추가해보려 한다. 주문 개수 기능 추가, 상품 옵션 기능 추가, 총 판매 조회 기능 추가, 총 판매 상품 목록 조회 기능 추가... 음;;; 이건 시작부터 판짜고 들어가야 했었다.. ㅋㅋㅋ 코드를 싹 손봐야할수도;; 일단 필수 기능은 다 구현했으니 만족한다.