코드스테이츠 교육과정에 포함되어 있던 프로젝트를 다시 해보고자 한다.
(클론 코딩)
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클래스 작성
3-3. Side클래스 작성
3-4. Drink클래스 작성
3-5. BurgerSet클래스 작성
모든 상품 클래스에서 속성(필드)들의 접근 범위를 private으로 지정해 두고 나서 Getter 및 Setter를 통해서만 상품의 필드에 접근할 수 있도록 해두었는데, 이는 캡슐화와 관련이 있다.
4.디렉터리 정리
자바에서 패키지는 특정 목적을 공유하는 클래스 및 인테페이스들을 묶은 것을 의미한다.
밑에 사진에 나온 경로랑 똑같이 패키지를 생성한다.
왜 이런식으로 com.codestates.seb.burgerqueen이라는 이름의 패키지로 감싸는지 의문이 생길수 있다.
이전에 상태에서는 바로 src밑에 클래스가 있었는데 이러한 경우,해당 클래스들은 default 패키지로 자동으로 포함되게 되는데, 자바에서는 원칙적으로 default 패키지의 클래스들을 다른 클래스에서 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' 부분은 '저장'만 한 것이기 때문에 문제가 없다.
왜? main 메서드에 프로그램 로직코드를 작성하지 않을까? 라는 의문이 생길수도 있다.
static이 있는 클래스 메서드에서는 static이 있는 클래스 변수만 사용이 가능하다. 인스턴스 변수가 사용이 불가능하기 때문에 따로 클래스를 만든것이다.
Menu 객체
메뉴 출력하기 → printMenu()
Cart 객체
장바구니 담기 → addToCart()
옵션 고르게 하기 → chooseOption()
햄버거 세트 구성하기 → composeSet()
장바구니 출력하기 → printCart()
Order 객체
주문하기 → makeOrder()
burgerqueen 패키지 내에 Menu 클래스를 생성
Menu 객체는 메뉴를 출력 해주는 기능을 수행한다. 따라서, 전체 상품 정보를 필드로 가지고 있을 필요가 있다.
ProductRepository의 products는 private으로 접근 제어자가 지정되어 있다. 외부에서 products에 접근할 수 있도록 Getter 역할을 하는 getAllProducts() 메서드를 만들어준다.
Menu 클래스에는 printMenu() 메서드가 필요하며, printMenu()는 아래 그림처럼 카테고리별로 메뉴를 출력해주어야 한다.
메서드의 반환값과 입력값을 확실히 정하여야 한다.
printMenu()의 역할은 메뉴를 카테고리별로 출력할 수 있어야 하며, 장바구니를 출력하는 버튼 및 주문 버튼까지 출력해 주면 된다
입력값 : printMenu()가 출력할 대상은 Menu 클래스의 필드인 products이며, Menu 클래스 내에 printMenu()를 정의할 것이므로, 입력값이 따로 필요하지 않다.
반환값 : 단순히 출력만 해주는 메서드이므로, 반환값이 필요하지 않다.
printMenu()는 OrderApp에서 호출할 것이므로, 접근 제어자는 public으로 지정해주어야 한다.
대략적인 형태를 만들어보자!
다음은 Menu 클래스의 products 필드를 반복문으로 순회하면서, 각각의 카테고리에 맞는 상품들을 출력해 주면 된다.
instanceof 연산자를 활용하여 products배열의 각 요소가 햄버거인지, 사이드인지, 음료인지를 판단한다.