Java 키오스크 과제 트러블슈팅 + 피드백

ssongyi·2025년 3월 14일
0

개발환경 - IndelliJ IDEA / JDK 17

필수기능

Lv 1. 기본적인 키오스크를 프로그래밍해보자

  • 햄버거 메뉴 출력 및 선택하기

Lv 2. 객체 지향 설계를 적용해 햄버거 메뉴를 클래스로 관리하기

  • MenuItem 클래스 생성하기
  • main 함수에서 MenuItem 클래스를 활용하여 햄버거 메뉴를 출력합니다.

Lv 3. 객체 지향 설계를 적용해 순서 제어를 클래스로 관리하기

  • Kiosk 클래스 생성하기
  • 키오스크 프로그램을 시작하는 메서드가 구현되어야 합니다.

Lv 4. 객체 지향 설계를 적용해 음식 메뉴와 주문 내역을 클래스 기반으로 관리하기

  • Menu 클래스 생성하기

Lv 5. 캡슐화 적용하기

  • MenuItem, Menu 그리고 Kiosk 클래스의 필드에 직접 접근하지 못하도록 설정합니다.
  • Getter와 Setter 메서드를 사용해 데이터를 관리합니다.

도전기능

Lv 1. 장바구니 및 구매하기 기능을 추가하기

  • 장바구니 생성 및 관리 기능
  • 장바구니 출력 및 금액 계산
  • 장바구니 담기 기능
  • 주문 기능

Lv 2. Enum, 람다 & 스트림을 활용한 주문 및 장바구니 관리

  • Enum을 활용한 사용자 유형별 할인율 관리하기
  • 람다 & 스트림을 활용한 장바구니 조회 기능

String 은 Java 의 내장 클래스

private string name;

으로 속성을 정의했을 때,
Cannot resolve symbol 'string' 이라는 에러가 났다.

double 은 소문자로 명명해도 되는데, 왜 string 은 대문자로 해야하지? 싶어서 확인해본 결과 이유는 아래와 같다.

  1. String 은 Java 의 내장 클래스이다.
  2. Java 에서 String 은 기본 데이터 타입이 아닌 클래스이다. 모든 클래스 이름은 대문자로 시작하는 것이 Java의 명명 규칙이다.
  3. Double 래퍼 클래스를 사용할 수는 있지만, 일반적인 수치 계산은 기본 타입인 double 을 사용하는 것이 더 효율적이다.

에러를 해결한 코드를 함께 첨부했다.


'생성자'와 'Getter' 의 new ArrayList 의 차이

리팩토링을 하다가 궁금한 점이 있었다.

이미 생성자에서 this.items = new ArrayList<>(); 으로 리스트 복사본을 return 하는거 아닌가?

getItems 에서 복사본을 또 만들어서 캡슐화를 강화해야 하나?

이는 두 복사본이 다른 목적을 가지고 있다고 한다.

생성자의 this.items = new ArrayList<>();

  • 이는 Menu 객체가 생성될 때 items 필드를 초기화하는 것
  • 빈 ArrayList 를 생성하여 items 에 할당함

getItems()의 return new ArrayList<>(items);

  • 이는 외부에서 items 리스트에 직접 접근하여 수정하는 것을 방지하기 위한 것
  • 원본 리스트의 복사본을 반환함으로써 캡슐화를 강화

만약 return items;로 직접 반환한다면, 외부에서 이 리스트를 수정할 수 있게 되어 Menu 클래스의 내부 상태가 예기치 않게 변경될 수 있다.

! 생성자의 new ArrayList 는 복사본을 만드는 것이 아니었다..
! 그저 새로운 ArrayList 객체를 초기화하는 용도였다...
(객체를 처음 만들 때 사용되는 방식)

  • 초기화
    : new ArrayList<>() 는 비어있는 새로운 ArrayList 객체를 생성
    : 이는 내부적으로 기본 크기(일반적으로 10) 의 빈 배열을 만듦
  • 용량 설정
    : 생성자를 통해 초기 용량 지정 가능
    : ex ) new ArrayList<>(20) = 초기 용량 20 인 ArrayList
  • 다른 컬렉션으로부터 생성
    : new ArrayList<>(Collection c) 와 같이 다른 컬렉션의 요소들로 초기화된 ArrayList 생성 가능
  • 내부 구조
    : ArrayList 는 생성 시 elementData 라는 Object 배열을 할당받아 요소들을 저장함

IntStream 활용 이유

처음에는 Stream<Integer> 을 활용해서 구현했다가, 리팩토링을 하던 와중 의아한 점이 있었다.
왜 굳이 IntStream 을 사용해야 하지?

IntStream 이란?

  • Java8 에서 도입된 Stream API 의 일부
  • 정수(int) 기본 타입에 특화된 스트림

일반 Stream<Integer> 보다 효율적인 이유

  1. 성능 향상
    : 기본 타입인 int 값 직접 다루므로 박싱/언박싱 필요 X
  2. 특화된 연산
    : 정수에 특화된 연산 메서드 (예: sum(), average(), range()) 를 제공
  3. 메모리 효율성
    : 기본 타입을 사용하므로 객체를 저장하는 것보다 메모리 사용이 적음
  4. 범위 생성의 편리성
    : IntStream.range() 또는 rangeClosed() 메서드를 사용하여 쉽게 연속된 정수 범위 생성 가능
  5. 병렬 처리의 용이성
    : IntStreamparalle() 메서드를 통해 쉽게 병렬 처리 가능

IntStream.range()

  • 정수 범위 생성
    : 지정된 시작값(포함)부터 종료값(미포함)까지 연속된 정수 스트림 생성
  • rangeClosed 와의 차이
    : range() = 종료값 미포함 / rangeClosed() = 종료값 포함

printf 메서드 ?

System.out.printf("%d. %-14s | W %.1f | %s%n",
i + 1,
item.getName(),
item.getPrice(),
item.getDescription());

println 만 쓰다가 printf 를 처음 쓰게 됐다.
정확한 정의와 어떤 상황에서 쓰는지 조금 더 조사하게 됐다.

printf

  • Java 에서 형식화된 출력(Formatted Output) 을 제공하는 메서드
  • System.out.prinft() 는 특정 서식(format) 에 따라 데이터를 출력할 수 있도록 설계됨
  • C 언어의 printf 함수와 유사한 역할

기본 문법

System.out.printf("출력 서식", 출력할 내용);

특징

  1. 형식화된 출력
    : 특정 형식에 맞춰 데이터 출력 가능
    : ex) 소수점 자릿수 지정 or 문자열의 길이 맞추기
  2. 여러 데이터 타입 처리
    : %d(정수), %f(실수), %s(문자열) 등의 형식 지정자를 사용하여 다양한 데이터 타입을 한 번에 출력 가능
  3. 정렬과 간격 조정
    : 출력 데이터의 정렬이나 간격을 쉽게 조절 가능
  4. 줄바꿈 없음

printf 를 주로 사용하는 상황

  • 표 형태의 데이터 출력
  • 소수점 자릿수 제어가 필요한 경우
  • 여러 데이터를 특정 형식에 맞춰 출력해야 할 때
  • 출력 결과의 정렬이 중요한 경우

enum 상수 정의 + 인스턴스 변수(필드) 선언 + 생성자 초기화 에러

1. 상수에 인자를 전달하고 있지만, 저장할 공간이 없어서 나는 에러

enum 상수를 선언했다.
그런데 Expected no arguments but found 1 에러가 났다.

해당 에러가 나는 이유는 아래와 같다.

enum 상수에 인자를 전달하고 있지만, 해당 인자를 받아들일 생성자가 정의되어 있지 않기 때문이다.

enum 인자를 전달하려면 다음과 같은 작업이 필요하다.

  1. enum 내부에 필드 선언
  2. 해당 필드를 초기화할 생성자 정의

사실 잘 이해가 되지 않았다.

상수로 이미 속성을 선언하고 있는 것 아닌가?
왜 한 번 더 필드 선언을 해주어야 하는걸까?

이것은...

  1. enum 상수 정의(SOLDIER(0.2) 등) 은 단순히 값을 전달하는 것이다.
    이것만으로는 그 값을 저장하거나 사용할 수 없다.
  2. discountRate 라는 필드가 필요한 이유
    : 이 필드는 enum 상수에 전달된 값을 실제로 저장한는 공간
    : 이 필드가 없다면, 전달된 값은 어디에도 저장되지 않고 사라진다.

즉, enum 상수 정의 = 값을 '전달'

discountRate 필드 = 값을 '저장'

이렇게 생각하니까 그나마 조금 이해가 되는 것 같다...

2. enum 상수의 생성자가 없어서 나는 에러

그래서 상수 선언 + 필드 정의 까지 했는데도 에러가 났다.

Variable 'discountRate' might not have been initialized

이 에러가 발생하는 이유는,
enum 상수에 대한 생성자가 정의되어 있지 않기 때문이라고 한다.

enum 에서 상수를 선언할 때 괄호 안에 값을 넣으면,
그 값을 받아 초기화할 수 있는 생성자가 필요한데 생성자가 없기 때문에 나는 에러라고 볼 수 있었다.

생성자를 추가함으로써:

  1. enum 상수 선언 시 전달된 값을 받아 처리 가능
  2. discountRate 필드 초기화 가능

3. enum 의 생성자를 public 으로 생성하지 않는 이유

enum 생성자는 기본적으로 private 하며,
명시적으로 선언하지 않아도 된다.

enum 은 고정된 상수 집합을 나타내기 위한 특별한 형태의 클래스이다.
외부에서 새로운 인스턴스를 생성하는 것을 방지하기 위해 생성자를 private 로 제한해야 한다고 한다.

또한 컴파일러가 public 생성자를 허용하지 않는다.
enum 의 생성자를 public 으로 선언하려고 하면 컴파일 오류가 발생한다.


과제 피드백

완성도 피드백

이해도 피드백

우수성 피드백

정리

  1. printf 를 사용한 포맷팅도 좋지만, toString을 재정의하면 코드의 파편화 막을 수 있음
  2. kiosk 역할을 input, output class 로 나누어서 더 명확하게 의존성 관리하기
  3. 패키지명을 자세하게 써 놓을수록 import 를 보고 클래스 활용이 가능해짐
  4. JavaDoc 으로 주석 능력 기르기

0개의 댓글