오늘은 내가 고민했던 부분들을 써내려 갈 것이다 !
1. Main 의 역할
1) 문제상황
- add
한 부분을 Main
에 하는게 맞는지 궁금하다. 현재는 이러한 상태로 main
에다가 직접 객체를 삽입하고 있다. 문제상에서도이렇게 적혀있어서 넣긴 하는데 밖으로 빼는게 나을까?
2) 문제 분석
- 일단 add
를 Main
에 해도 된다. 과제 수준에서는 이렇게 하는 것이 최선이다. 정 하고 싶다면 이걸 별도의 클래스에서 관리해도 되지만 과제에서 요구하는 바는 이 정도인 거 같았다.
3) 문제 해결
- 해당 부분은 일단 추후 마지막 도전과제를 모두 끝낸 후에 별도 클래스를 추가하는 걸 고려하기로 했다.
2. 카테고리 객체 생성
1) 문제상황
Menu burger = new Menu();
burger.addMenuItems(new MenuItem("Whopper",7100,"**"));
- 현재는 카테고리 메뉴도 객체로 만들어져 있고, 메뉴아이템도 객체로 만들어져 있는 상태이다.
- 다른 조원들은 이런식으로 하지 않고 직접 menuItems
에 카테고리를 추가하는 형태로 진행한 거 같았다.
- 내 생각에는 나중에 삽입하고 연결하는 객체 지향적으로 분석하면.. ? 이렇게 개별 객체로 두고 그 객체를 삽입하는 식으로 하는 게 나을 거 같았다. 하지만, 이것은 내 생각이다. 요구 사항에 적절한지 의문이 생겨서 해결하고자 했다.
2) 문제 분석
- 카테고리 객체를 만드는 것도 적절한 방법이라고 한다. 따로 튜터님이라면 어떻게 해결할 건지 여쭈어 보았다. 튜터님 같은 경우에도 카테고리 객체 생성하고, 메뉴아이템에 카테고리도 넣을 거 같다라고 하셨다.
3) 문제 해결
- 해당 부분은 마지막 도전과제를 다 하고 수정해보고자 한다. 카테고리만 메뉴아이템에 추가하는 방향으로 하고자 한다.
3. Input Error 처리 방향
1) 문제상황
- input 도 별도의 클래스로 구분하고자 한다. 왜냐면 계속해서 반복하기 때문이다.
- 그런데 꽤 복잡하다 생각했다. 애초에 에러가 많이 나는 Input Scanner 를 다른 클래스로 분리하면 실제 반복문에서는 해당 에러를 어떻게 처리할지 감이 안 잡혔다. 그렇게 되면 중복되는 에러처리이지 않을까? 라는 생각에 고민을 해보았다.
2) 문제 해결
- 일단은 해결 방법은 두가지이다.
- 첫 번째는 에러가 뜰 때 input
클래스에서 boolean
값을 전해주는 것이다. 그래서 실제 반복문에서는 if
문을 통해서 정상일 때는 정상처리를 해주고, 아니면 오류를 던져주는 식으로 처리하는 방법
- 두 번째는 지금처럼 try Catch
문이지만, 키오스크 코드에다가 한 종류의 에러만 출력되게끔 하는 것이다. 그러니까 input Error
로 나올 수 있는 Error
는 하나의 에러로 묶어서 처리하는 것이다.
- 나는 두 번째 방식을 채택했다. 이게 이렇게 처리하다보니, scanner
의 고질적인 버퍼 문제 때문에 ! 모든 catch
문에 버퍼를 지워주는 로직을 세워야했다.
public static int getInput(){
try {
return sc.nextInt();
} catch (InputMismatchException e) {
Output.printOutput("숫자를 입력하셔야 합니다.");
Input.printNextLine();
throw new RuntimeException();
} catch (NullPointerException e){
Output.printOutput("입력값이 비어있습니다.");
Input.printNextLine();
throw new RuntimeException();
} catch (NumberFormatException e) {
Output.printOutput("유요한 숫자가 아닙니다.");
Input.printNextLine();
throw new RuntimeException();
}
}
4. Getter 활용
1) 문제 상황
// 번호를 받으면 menu.items Get
List<MenuItem> itemList = menu.getMenuItems();
System.out.printf("선택한 메뉴: %s | %s | %s \n",itemList.get(orderMenuItem-1).menuName,itemList.get(orderMenuItem-1).menuPrice,itemList.get(orderMenuItem-1).menuDescription);
- 리스트를 변수에 저장한 후 리스트의 값을 출력해주는 코드다.
- 근데 이걸 굳이 리스트로 받아오는 게 객체지향적인가? 라는 의문이 생겼다.
2) 문제 해결
menu.displaySelectedMenu(orderMenuItem-1);
- 해당 부분을 Getter
함수(이름은 조금 더 직관적으로 수정했다.)로 활용하여 나타내었다. 확실히 Kiosk
클래스에서 한줄로 줄어들어 코드의 흐름이 한눈에 보였다.
5. 장바구니 아이템과 장바구니 객체
1) 문제 상황
- 장바구니 라는 기능을 만들기 위해 두가지를 고민했다.
- 첫 째, 냅다 리스트에 모든 값을 넣는 것. -> 이중 리스트 고려
- 둘 째, 리스트와 리스트 안의 객체를 별도로 생각하여 각각의 클래스 생성할 것
2) 문제 분석
- 일단은, 리스트에 모든 값을 넣는 것을 상상해보자. 그렇다면 이중리스트로 구현해야 할 것이다. 혹은 HashMap
으로 구현하는 것을 생각해보아야 한다. 그렇다면 과연 객체지향적인가? HashMap
을 통해서 구현을 한다면 확실히 컬렉션에 대한 이해도를 챙길 수 있어서 좋을 거 같긴하다. 하지만 문제에서도 정의되어 있지 않았는가? 실제로 Set<String> keys = memberMap.keySet();
을 이용해서 HashMap
의 Key
와 장바구니 속 Key
와 매치해도 괜찮을 거 같았다.
- 이럴 때에는 목적과는 조금 다른 거 같다. 자료구조를 활용하여 기능을 구현하는 것도 개발자에게 중요한 역량이고, 객체 지향을 활용하여 구현하는 것도 개발자에게 중요한 역량이다. 이번의 과제의 의도, 즉 요구사항에서는 객체지향에 대해 공부하고 그것을 이해하는 부분에서의 의의가 크다. 그래서 이번에는 객체지향을 위해서 각자의 객체를 두고 그것을 연결하는 방법을 선택하고자 했다!
- 두 개의 클래스로 나누는 게 좋은 이유에 대해서는 아래와 같다 !
- 책임 분리 (SRP - 단일 책임 원칙)
Cart
클래스 → 장바구니 전체를 관리CartItem
클래스 → 개별 상품의 정보 저장
- 확장성과 유지보수성 향상
- -
CartItem
의 기능 확장 가능 - -
Cart
는 여러 개의CartItem
을 관리하는 역할로 분리되므로 코드가 간결해짐 -> 객체 지향적인 설
- -
- CartItem은 장바구니에 들어가는 개별 상품을 표현하는 객체
- Cart는 CartItem의 리스트를 관리하는 컨테이너 역할
3) 문제 해결
- 아래와 같이 분리해서 쓰는 걸로 해결하였다.
6. 다중 if 문의 코드 복잡성
1) 문제상황
- if
문이 많아져서 코드 복잡성이 증가했다. 이게 다른 사람이 이 코드를 읽었을 때 좋은 코드인가? 아무리 생각해도 복잡해서 난해한 코드가 된거 같다.
case 4 -> { // 로직이 너무 복잡한데 . .음..
if(!cart.cartItemsNotEmpty()) {
throw new IndexOutOfBoundsException();
}
cart.showCartItems();
// 이거 4번을 누르면 새로 주문하는 클래스가 필요할 거 같단말이지.
Output.printOutput("1. 주문하기 | 2. 메뉴판으로 돌아가기");
// switch 문으로 한 번 더 만들기 이중 switch 문...
int orderConfirmation = Input.getInput();
if (orderConfirmation==1) {
// switch
Output.printfStringOutput("주문이 완료되었습니다.","총 금액: "+Integer.toString(cart.getTotalPrice()));
isRunning = false;
continue;
} else if ( orderConfirmation==2 ){
System.out.println("메뉴판으로 돌아갑니다.");
continue;
} else {
throw new IndexOutOfBoundsException();
}
}
- 해당 로직을 어떻게하면 난해하지 않는 코드로 바꾸지에 대해 고민을 해보았다.
2) 문제 분석
- if
문을 너무 사용하게 되면, 코드 복잡성이 올라간다. 이러할 경우에는 코드리펙토링 과정을 통해서 중첩 조건문을 수정해야한다.
3) 문제 해결
case 4 -> {
if (!cart.cartItemsNotEmpty()) {
throw new IndexOutOfBoundsException();
}
cart.showCartItems();
Output.printOutput("1. 주문하기 | 2. 메뉴판으로 돌아가기");
int orderConfirmation = Input.getInput();
switch (orderConfirmation) {
case 1 -> {
Output.printfStringOutput("주문이 완료되었습니다.", "총 금액: " + Integer.toString(cart.getTotalPrice()));
isRunning = false;
continue;
}
case 2 -> {
System.out.println("메뉴판으로 돌아갑니다.");
continue;
}
default -> throw new IndexOutOfBoundsException();
}
}
- 이러할 경우에는 이중 스위치문을 사용하면 된다. 확실히 위의 if
문 보다는 조금 더 직관적으로 읽을 수 있는 것을 알 수 있다 !
7. Final
이 왜 필요한가 ?
- Final
에 대해서 알아볼려면 불변성에 대해서 알아야한다.
1 불변성
1) 불변성이란?
" 한 번 값이 정해지면 절대 변경할 수 없는 상태 "를 의미한다.
즉, 객체나 변수의 값을 변경할 수 없도록 보장하는 개념이다.
2) 참조(Reference) 자체가 변경되지 않도록 보장
- final을 사용하면 변수의 참조(Reference) 자체가 변경되지 않도록 보장한다.
- 하지만! 객체 자체가 불변인지 여부는 따로 관리해야 한다.
= > setter를 사용하면 final의 의미가 사라져서 불변 객체가 아니게 된다
3) 완전한 불변 객체(Immutable Object)
- 객체의 값을 절대 변경할 수 없게 하려면
> 모든 필드를 private final로 선언
> 생성자로 초기화 후 setter 제공하지 않기
> 변경 가능한 필드는 복사본을 제공 (방어적 복사, Defensive Copying)
4) 객체의 final
- 객체의 final
인 경우 조금 다른 의미를 가진다.
기본 타입 (int
, double
, boolean
등) → final
을 쓰면 값 자체를 변경할 수 없음.
참조 타입 (List
, Map
, Set
, ArrayList
등) → final
을 써도 객체 내부 값은 변경할 수 있음. → 단, 새로운 객체로 할당(new
)은 불가능함!
- 그러니까 객체에 final
을 선언하게 되면, 메모리가 stack
에 쌓이고 해당 객체의 인스턴스 값 즉 실제 리스트의 값들은 Heap
저장된다. 여기서 Heap
에 저장되는 아이들은 수정이 가능한데 이러한 성격 때문에 내부 아이템은 변경이 가능하다는 말이 나오는 것이다.
- 정말 불변 리스트를 만들고 싶은 것이라면, Collections.unmodifiableList(list)
를 반환하면 된다. 이러면, 외부에서 값을 변경할 수 없다.
8. 패키지 분리 방법 추천
- 도메인 단위> 도메인 단위로 나누기
- 레이어 > 서비스, 컨트롤러 등의 레이어로 분리
✅ 오늘의 회고
- 도전과제 1단계까지는 끝이다 : >
- 내일은 2단계까지 끝내는 것이 목표이다 !! 내일도 파이팅 해봐야겠다 😆
- 이해가 안 가서 이렇게 펼쳐서 생각해보니 이해가 잘 안됐던게 이해가 됐다. !! 😇😇
'백엔드 부트캠프 > TIL' 카테고리의 다른 글
[내일배움캠프Spring-16일차] Kiosk 기능 구현 (0) | 2025.03.11 |
---|---|
[내일배움캠프Spring-14일차] 키오스크 문제 (0) | 2025.03.07 |
[내일배움캠프Spring-13일차] Thread (1) | 2025.03.06 |
[내일배움캠프Spring-12일차] Enum (1) | 2025.03.05 |
[내일배움캠프Spring-11일차] DFS 이해하기 (1) | 2025.03.04 |