지금까지 작성한 코드는 장바구니에 상품을 담을 때, 새로운 객체를 만들어서 담는 것이 아니라, productRepository에 미리 만들어둔 객체를 담고 있다.
해당 기능을 수행하는 메서드인 addToCart()를 살펴보자.
addToCart()는 상품의 id값을 인자로 받아와, 인자를 통해 상품을 productRepository에서 검색한 다음, 해당하는 상품을 product에 할당한다.
그러고 나서, 옵션을 선택하거나 세트를 구성하는 등의 동작을 마친 뒤, items 배열의 길이를 확장하고, product를 items에 추가한다.
즉, 사용자가 고른 상품을 새로운 객체로 만들어 장바구니에 추가하는 것이 아니라, productRepository에 저장된 객체의 참조값을 그대로 장바구니에 담게 되며 이러한 로직은 동일한 상품을 두 번 고르면서, 옵션은 다르게 고르는 경우에 문제를 발생시킵니다.
ex1)새우버거세트(감자튀김(케첩 3개), 제로콜라(빨대 있음))를 고른 다음, 감자튀김(케첩 1개)을 장바구니에 추가하면, 새우버거세트에 포함된 감자튀김의 케첩 개수도 1개가 된다.
ex2) 새우버거세트(감자튀김(케첩 3개), 제로콜라(빨대 있음))을 고른 후, 새우버거세트(감자튀김(케첩 5개), 제로콜라(빨대 없음))을 추가하면, 먼저 추가한 새우버거세트의 사이드와 드링크의 옵션이 마지막으로 추가한 새우버거세트의 항목으로 덮어 쓰인다.
앞서 정의한 문제의 핵심은 장바구니에 상품을 담을 때, 새로운 객체를 생성해서 담는 것이 아니라, 각 상품 객체의 참조값을 장바구니에 그대로 담는다는 데에 있다.
일단, 상품을 장바구니에 담는 메서드는 앞서 살펴본 것처럼 addToCart()이므로, addToCart()에서 새로운 상품 객체를 생성하여 items에 담을 수 있도록 코드를 수정해야 한다.
일단 수정하기 전에 얕은 복사와 깊은 복사에 대해 알고가야 한다.
얕은 복사는 객체의 참조값을 복사하는 것을 의미한다.
아래의 예제를 보면, newProduct에 product를 할당하고 있다.
ex)public void shallowCopyExample(Product product) {
Product newProduct = product;
}
newProduct는 product에 할당되어 있던 주소값을 똑같이 할당받게 된다. 즉, newProduct와 product는 동일한 객체를 가리키고 있게 된다.
결과적으로, product를 통해서 객체의 필드를 수정하면 newProduct의 필드도 똑같이 변화한다. 둘이 가리키는 객체가 결과적으로 같기 때문이다.
반면, 깊은 복사는 내용이 동일하지만 참조값이 다른 새로운 객체를 생성하는 것을 의미한다.
말 그대로, 같은 내용을 가진 다른 객체를 복제해 내는 것이다.
ex2)public void DeepCopyExample(Product product) {
Product newProduct = new Product(product.getId, product.getName, ...);
}
이런식으로 오버로딩을 해준다.
위와 같이 깊은 복사를 수행해 주는 생성자를 복사 생성자라고 한다.
이제 다시 main() 메서드를 실행하고, 동일한 상품의 옵션을 다르게 설정해도 잘 적용되는 것을 확인할 수 있다.
일단 burgerqueen패키지에 Order클래스를 만들자
주문은 장바구니에 담긴 상품을 기반으로 이루어진다. 따라서 Order 클래스에는 cart 객체를 통째로 필드로 정의.(할인과 관련된 코드는 추후에 작성)
makeOrder()는 장바구니의 상품들을 주문하는 기능을 수행
주문과 관련하여는 주문 내역을 출력하는 것만 요구하고 있다.
따라서, 별다른 작업 없이 주문 내역을 요구 사항에서 요구하는 대로 출력해 주도록 메서드를 정의하면 된다.
필드로 정의한 cart의 items에 있는 상품들을 형식에 맞게 단순히 출력해 주면 되므로, 입력값은 필요하지 않으며, 출력값 또한 필요하지 않습니다. 또한, OrderApp에서 호출할 수 있어야 하므로, 접근 제어자는 public이어야 할 것입니다.
부족한 부분이 두가지가 있다.
1.상품 상세 내역 출력 --> cart.printCartItemDetails()를 재사용
2.금액 합계 계산 --> cart.calculateTotalPrice()를 재사용
Order에서도 cart의 메서드를 호출할 수 있게 하려면 이 둘의 접근 범위를 public 또는 protected로 지정해주어야 한다.