Project: BurgerQueen (1)

SJW·2023년 6월 19일
0

코드스테이츠 교육과정에 포함되어 있던 프로젝트를 다시 해보고자 한다.
(클론 코딩)

  1. 공통점과 차이점 먼저 생각하자
    일단, 클래스는 객체를 만들어 내는 일종의 틀이다.
    그렇다면, 어떤 클래스를 어떻게 만들지 결정하기 위해서는 어떤 객체가 필요한지 먼저 알아야 한다.
    무엇을 객체로 만들지는 요구사항에서 어떤 개념이 존재하는지 파악한다.
    각 상품들이 존재하고 상품들은 햄버거, 사이드, 음료라는 카테고리로 구분되고 있다.
  • 공통점은 상품들이 이름, 열량, 가격, 고유번호를 가진다.
  • 차이점은 각 카테고리별 선택할 수 있는 옵션이 다르다.
  1. 클래스 상속 구조 설계하기
    공통점과 차이점 둘 다 중요하지만, 조금 더 집중할 부분은 공통점이다.
    이 부분에서는 상속과 추상화의 개념이 이용된다.
  • 하위클래스 extends 상위클래스 일 때, 하위클래스가 상위클래스를 “상속받고 있다” 또는 “확장한다”라고 표현하며, 이때, 하위클래스를 통해 상위클래스의 필드와 메서드를 사용할 수 있다.
  • 추상화 : 클래스A, 클래스B, 클래스C가 공통적인 속성이나 메서드를 가지고 있을 때, 공통적인 속성 또는 메서드를 추출하는 것을 의미한다.

class Product {

int id;      // 모든 상품의 공통적인 속성
String name; // 모든 상품의 공통적인 속성
int price;   // 모든 상품의 공통적인 속성
int kcal;    // 모든 상품의 공통적인 속성

... 

}

class Hamburger extends Product {

boolean isBurgerSet; // 햄버거의 옵션 : 세트 여부
int burgerSetPrice;  // 햄버거 세트 옵션을 표기할 때 필요한 세트의 가격 : 아래 설명 참고
	
...

}

class Side extends Product {

int ketchup; // 사이드의 옵션 : 케첩 개수

...

}

class Drink extends Product {

boolean straw; // 음료의 옵션 : 빨대 유무

...

}

class BurgerSet extends Product {

Hamburger hamburger; // 포함 관계 : 아래 설명 참고
Side side;           // 포함 관계 : 아래 설명 참고
Drink drink;         // 포함 관계 : 아래 설명 참고

...

}

추상화를 통해 공통점을 product 클래스에 정의하고 각각의 클래스와 상속관계를 만든다.
그리고 각각의 클래스에는 차이점을 정의한다.
Hamburger 클래스에 burgerSetPrice가 필요한 이유: 처음에 햄버거를 선택할때는 단품가격만 반영된 햄버거가 보이고 햄버거를 선택하면 세트가격을 표시해서 물어보기 때문에 burgerSetPrice 변수가 있다.
또한 BurgerSet클래스에는 포함관계를 사용했다.
포함관계: EX) "~은~이다." -->상속 , "~은 ~을 가지고 있다." -->포함관계

3-1. Product클래스 작성

3-2. Hamburger클래스 작성

  • Product클래스와 동일하게 생성자와 getter,setter 이용한다.
  • super()는 상위클래스의 생성자를 호출,즉 상속관계를 전제로 하며 생성자 내부에서만 쓰이고 반드시 생성자의 첫번째 줄에 위치하여야 한다.(this() 매서드와 매우 비슷)

3-3. Side클래스 작성

3-4. Drink클래스 작성

  • Getter의 이름을 의미에 부합하게 hasStraw로 한다.

3-5. BurgerSet클래스 작성

  • 포함관계를 위주로 생각한다. 즉, BurgerSet클래스는 Hamburger,Side,Drink클래스를 가지고 있다.
  • 포함관계: EX) "~은~이다." -->상속 , "~은 ~을 가지고 있다." -->포함관계
  • 이후에 세트를 구성할 때 생성자를 통해서 세트를 구성할 것이므로, Setter는 만들지 않아도 된다. 생성자와 Getter만 추가하자

모든 상품 클래스에서 속성(필드)들의 접근 범위를 private으로 지정해 두고 나서 Getter 및 Setter를 통해서만 상품의 필드에 접근할 수 있도록 해두었는데, 이는 캡슐화와 관련이 있다.

4.디렉터리 정리
자바에서 패키지는 특정 목적을 공유하는 클래스 및 인테페이스들을 묶은 것을 의미한다.
밑에 사진에 나온 경로랑 똑같이 패키지를 생성한다.

왜 이런식으로 com.codestates.seb.burgerqueen이라는 이름의 패키지로 감싸는지 의문이 생길수 있다.
이전에 상태에서는 바로 src밑에 클래스가 있었는데 이러한 경우,해당 클래스들은 default 패키지로 자동으로 포함되게 되는데, 자바에서는 원칙적으로 default 패키지의 클래스들을 다른 클래스에서 import 할 수 없다.

  • 간단한 테스트를 해보자.
    이전과 마찬가지로 src 바로 밑에 클래스를 만들고 Main클래스에서 Test클래스를 import를 해보자

5.이제부터는 요구사항에 나타나 있는 대로 상품 정보를 입력해서 인스턴스화시켜 두고, 상품 저장소 역할을 하는 객체를 만들자

ProductRepository에 필드로 배열 변수를 정의해서 모든 상품을 이 배열 안에 포함시켜 관리할 예정이다.
단 문제가 있다. 배열의 동일한 타입의 데이터들만 담을 수 있는데, 배열에 넣을 상품들의 타입이 다르다는 것이다.
예를 들면 새우버거 인스턴스는 Hamburger 타입, 감자튀김 인스턴스는 Side 타입, 코카콜라 인스턴스는 Drink 타입이라는 것이다.
하지만 다형성을 활용하면 해결할수 있다.
다형성 : 상위클래스 타입의 참조 변수로 하위클래스 타입의 객체를 참조할 수 있는 것

Hamburger, Side, Drink 클래스는 Product 클래스를 상속받고 있으므로, Product 클래스가 이들의 상위클래스가 되며, 따라서 Hamburger, Side, Drink 인스턴스는 아래와 같이 Product 타입의 변수에 할당할 수 있습니다. 이를 업캐스팅이라고 한다.


그렇다면 예를 들어
public class ProductRepository {
private final Product[] products = {
new Hamburger(1, "새우버거", 3500, 500, false, 4500),
new Hamburger(2, "치킨버거", 4000, 600, false, 5000)}}
이런식의 배열 형태로 저장을 하였다고 한다면 다형성으로 인해
new Hamburger(1, "새우버거", 3500, 500) 여기까지만 저장이 되고 false랑 4500은 저장을 못하는거 아닌가???


다시 한번 작성하자면 다형성은 상위 클래스 타입의 참조 변수로 하위 클래스 객체를 참조하는 것! 그리고 이때 상위 클래스 타입의 참조 변수가 '사용'가능한 멤버의 개수는 상위 클래스의 멤버 수나 된다. 즉, 사용과 저장은 다르다. 그러므로 ProductRepository에 'false, 4500','false, 5000' 부분은 '저장'만 한 것이기 때문에 문제가 없다.

  • 참고
    상품 정보는 프로그램 실행 중 바뀌는 값이 아니므로, 그리고 바뀌어서도 안 되므로 상수로 만들어 준다.
    햄버거 세트는 사용자의 선택에 따라 구성하는 것이므로, 상품 저장소에 미리 만들어 두지 않는다.
    상품 옵션은 어차피 추후에 사용자로부터 입력받은 값으로 사용할 것이므로, 상품 옵션의 기본값은 아래와 같이 임의로 지정하면 된다.
    햄버거 : 단품 / 세트 여부 → 단품을 기본값으로 설정 → isBurgerSet을 false로 초기화
    사이드 : 케첩 개수 → 1개를 기본값으로 설정 → ketchup을 1로 초기화
    음료 : 빨대 유무 → 빨대를 제공하는 것을 기본값으로 설정 → hasStraw를 true로 초기화

프로그램 흐름정리

  1. 의사코드 작성하기
    OrderApp이 주문과정 전반을 관리한다.
  • 왜? main 메서드에 프로그램 로직코드를 작성하지 않을까? 라는 의문이 생길수도 있다.
    static이 있는 클래스 메서드에서는 static이 있는 클래스 변수만 사용이 가능하다. 인스턴스 변수가 사용이 불가능하기 때문에 따로 클래스를 만든것이다.

    OrderApp의 기능 분산

  • Menu 객체
    메뉴 출력하기 → printMenu()

  • Cart 객체
    장바구니 담기 → addToCart()

  • 옵션 고르게 하기 → chooseOption()

  • 햄버거 세트 구성하기 → composeSet()

  • 장바구니 출력하기 → printCart()

  • Order 객체
    주문하기 → makeOrder()

  1. burgerqueen 패키지 내에 Menu 클래스를 생성
    Menu 객체는 메뉴를 출력 해주는 기능을 수행한다. 따라서, 전체 상품 정보를 필드로 가지고 있을 필요가 있다.

  2. ProductRepository의 products는 private으로 접근 제어자가 지정되어 있다. 외부에서 products에 접근할 수 있도록 Getter 역할을 하는 getAllProducts() 메서드를 만들어준다.

printMenu()

Menu 클래스에는 printMenu() 메서드가 필요하며, printMenu()는 아래 그림처럼 카테고리별로 메뉴를 출력해주어야 한다.

메서드의 반환값과 입력값을 확실히 정하여야 한다.
printMenu()의 역할은 메뉴를 카테고리별로 출력할 수 있어야 하며, 장바구니를 출력하는 버튼 및 주문 버튼까지 출력해 주면 된다

입력값 : printMenu()가 출력할 대상은 Menu 클래스의 필드인 products이며, Menu 클래스 내에 printMenu()를 정의할 것이므로, 입력값이 따로 필요하지 않다.

반환값 : 단순히 출력만 해주는 메서드이므로, 반환값이 필요하지 않다.

printMenu()는 OrderApp에서 호출할 것이므로, 접근 제어자는 public으로 지정해주어야 한다.
대략적인 형태를 만들어보자!

다음은 Menu 클래스의 products 필드를 반복문으로 순회하면서, 각각의 카테고리에 맞는 상품들을 출력해 주면 된다.

instanceof 연산자를 활용하여 products배열의 각 요소가 햄버거인지, 사이드인지, 음료인지를 판단한다.

0개의 댓글