Kiosk

ChoRong0824·2025년 1월 20일
0

Java

목록 보기
40/41
post-thumbnail

Kiosk 기술 과제 트러블 슈팅


문득 궁금해진 것들.

상속지양

Cart클레스에 MenuItem을 상속 받아서 사용했었습니다.
그런데 이렇게 하게되면 이전에 제가 오브젝트 및 객체지향 관련 책을 읽었을 당시 상속을 하게 되는 경우를 최대한 지양해라라는 것이 기억이 낫습니다. 그래서 이런 코드를 짜게 되면 어떤 문제점을 초래하는 것일지 생각해봤습니다.

Cart extends MenuItem에 대한 문제점
상속을 지양하라는 이유 ?

  • 상속은 부모-자식 클래스 간 강한 결합도를 초래합니다.
  • 부모 클래스 변경 시 자식 클래스에도 영향을 미쳐 유연성이 떨어질 수 있기 때문이며,
    "is-a" 관계가 아니라 "has-a" 관계를 표현할 때 상속을 사용하면 설계가 잘못된 것입니다.

Cart extends MenuItem의 문제점

  • Cart는 장바구니이고, MenuItem은 메뉴 항목입니다. 이 둘은 "is-a" 관계가 아니며, "Cart has MenuItem" 관계로 표현하는 것이 올바르기 때문입니다.
    따라서 Cart가 MenuItem을 상속받는 것은 객체지향적으로 부적절합니다.

해결
Cart는 MenuItem을 포함하는 조합 관계로 설계하면 되는 것입니다
예) List<MenuItem> 또는 Map<MenuItem, Integer>


방어적 복사?

외부 코드가 내부 데이터를 직접 수정하지 못하도록 하는 기법입니다.
저의 코드에서

public List<T> getItems() {
    return new ArrayList<>(items); // 방어적 복사
}
  • new ArrayList<>(items)는 원본 데이터를 복사하여 반환하므로, 외부 코드가 반환된 리스트를 변경해도 원본 데이터에 영향을 미치지 않게 됩니다.
  • 이를 통해 방어적 복사는 불변성 유지, 데이터 무결성을 위해 자주 사용됌을 알 수 있었습니다.

HashMap과 Map의 차이?

이는 너무 당연하고 쉬운 내용이지만, 문득 List에서 Lsit<>로 해두고 ArrayList를 임포트 하면서 쓰는 것을 생각하면서 HashMap과 Map의 관계도 정리했습니다.

그래서 뭔데?

Map은 인터페이스이며, HashMap은 Map 인터페이스를 구현한 클래스입니다.

많고 많은 것중에 왜 Map을 사용하나요?

  • 유연성: 코드는 인터페이스에 의존하고, 구현체는 자유롭게 변경할 수 있도록 설계합니다.
    예를 들어, Map을 사용하면 나중에 TreeMap, LinkedHashMap 등으로 구현체를 변경할 때 코드 수정 없이 처리할 수 있습니다.
    Map<String, String> map = new HashMap<>(); // 인터페이스로 선언
    이렇게 작성하면 선언부는 Map에 의존하고, 실제 구현은 HashMap으로 처리됩니다.

그렇다면, 언제 HashMap을 직접 사용하나요?

특정 구현체의 메서드가 필요할 때만 HashMap을 사용합니다.


ArrayList와 List의 관계는?

ArrayList와 List의 관계

  • List는 인터페이스이고, ArrayList는 이를 구현한 클래스입니다.

왜 List를 사용하나요?

  • 유연성: List를 사용하면 LinkedList, Vector로 구현체를 바꿀 때도 코드 수정이 필요 없습니다.
    예를 들어, 아래와 같이 사용하면 선언부는 List에 의존하고, 구현부는 ArrayList로 처리됩니다.
    List<String> list = new ArrayList<>();

ArrayList를 직접 사용하는 경우
ArrayList의 특정 메서드가 필요하거나, 구현체를 변경할 필요가 없을 때 사용합니다.


왜 Cart는 서비스 로직이고, Menu는 DAO인가?

Cart가 서비스 로직인 이유

Cart는 비즈니스 로직(장바구니에 추가, 삭제, 가격 계산 등)을 수행합니다.
비즈니스 로직은 데이터를 처리하는 주된 역할을 하므로 서비스 계층에 위치합니다.

Menu는 데이터(메뉴)를 관리하는 역할만 수행합니다.
데이터의 CRUD(Create, Read, Update, Delete)를 담당하므로 DAO(Data Access Object)로 분류됩니다.


Cart에서 Map으로 필드 생성한 부분

(최종 리팩토링은 List)
기존 코드와 수정 코드의 차이
이전에는 List<CartItem> 구조를 사용했으나, 수정 후 Map<MenuItem, Integer> 구조를 사용했습니다.

차이점

  • List<CartItem> : 장바구니 항목을 CartItem 객체로 관리.
    장점: 장바구니 항목의 수량, 가격 등 다양한 정보를 추가로 저장 가능.
  • Map<MenuItem, Integer> : 메뉴와 수량만 관리.
    장점: 구현이 간단하고, 수량 관리에 적합.

어떤 방식이 더 적합한가?

장바구니 항목에 추가적인 속성(할인, 옵션 등)을 저장하려면 List<CartItem> 방식이 적합합니다.
단순히 메뉴와 수량만 관리하려면 Map<MenuItem, Integer> 방식이 적합합니다.

다시 말해,
간단한 요구사항/Map.
만약 장바구니에서 메뉴와 수량만 관리한다면 Map<MenuItem, Integer>이 더 적합합니다.
예를 들어, "햄버거 3개, 감자튀김 2개"처럼 간단히 저장할 때는 Map이 효율적입니다.

복잡한 요구사항/List
장바구니 항목에 추가 정보를 저장하거나 확장성이 중요하다면 List<CartItem> 방식이 더 적합합니다.
예를 들어, "햄버거 (치즈 추가, 매운맛 소스), 감자튀김 (라지 사이즈)" 같은 주문에서는 CartItem을 사용해야 합니다.


그래서 저는 다시 List<CartItem> 으로 사용했습니다.
유지보수성, 확장성 측면에서 해당 구조가 더 적합하다고 생각되었기 때문입니다.
이유)

  1. 확장성
    CartItem 객체에 수량 외에도 추가적인 속성(예: 옵션, 할인)을 쉽게 추가할 수 있으며 요구사항 변경에 유연하게 대처할 수 있습니다.
  2. 응집도
    MenuItem과 수량을 하나의 CartItem 객체로 관리하므로 논리적으로 데이터가 응집됩니다.
  3. 객체 지향 설계:
    CartItem은 "장바구니 항목"이라는 명확한 책임을 가지므로 설계가 더 명확해집니다.

결론

상속 대신 조합: Cart와 MenuItem의 관계는 "has-a" 관계로 표현해야 합니다.
방어적 복사: 외부 수정으로부터 데이터를 보호하는 중요한 기법입니다.
Map과 HashMap: 인터페이스(Map)를 사용하여 구현체를 유연하게 변경할 수 있습니다.
List와 ArrayList: 인터페이스(List)를 사용하면 유지보수성이 향상됩니다.
Cart와 Menu의 역할: Cart는 비즈니스 로직, Menu는 데이터 관리 역할입니다.
수정된 구조: List<CartItem>Map<MenuItem, Integer> 중 설계 요구사항에 따라 선택하면 됩니다.

profile
백엔드를 지향하며, 컴퓨터공학과를 졸업한 취준생입니다. 많이 부족하지만 열심히 노력해서 실력을 갈고 닦겠습니다. 부족하고 틀린 부분이 있을 수도 있지만 이쁘게 봐주시면 감사하겠습니다. 틀린 부분은 댓글 남겨주시면 제가 따로 학습 및 자료를 찾아봐서 제 것으로 만들도록 하겠습니다. 귀중한 시간 방문해주셔서 감사합니다.

0개의 댓글