자바로 CUI 키오스크를 만드는게 개인 과제로 나왔다.
과제를 제출하는데 어려웠던 기술적인 부분을 적어달라고 하길래 고민했던 것들을 적었다.
코드는 깃허브에 올렸다. https://github.com/OuOHoon/nbc-chapter2-homework
고민 목록
상태 관리
키오스크의 상태에 따라서 행동하기 위해서 개발하다보니 방대한 클래스를 만들었다. 이 클래스는 가독성이 나쁘고 기능을 추가하기 어려워서 어떻게 관리하는게 좋을지 고민했다. 키오스크의 상태에 따라 관련된 필드와 메소드를 클래스로 만들어서 캡슐화 했는데, 기능 추가가 쉬워지고 가독성은 좋아졌지만 그만큼 관리해야 하는 클래스가 늘어나서 불편함을 느꼈다.
// 상태 인터페이스
public interface KioskState {
String INPUT_TEXT = "번호를 입력해주세요. ";
void handleInteraction(KioskController kc);
}
// 컨트롤러
public class KioskController {
private Kiosk kiosk;
private KioskState kioskState;
private boolean isRun;
public KioskController() {
kioskState = new MainState();
kiosk = new Kiosk();
isRun = false;
}
public void run() {
isRun = true;
while (isRun) {
kioskState.handleInteraction(this);
}
}
public void setNextState(KioskState state) {
this.kioskState = state;
}
}
적절한 책임과 역할
객체가 다른 객체를 소유하지 않고 협력하기 위해서 어떤 구조가 필요한지 고민했다. MVC 패턴으로 모델, 뷰, 컨트롤러로 나눠서 만들었는데, 클래스에 적절한 책임을 부여하고 있는지 다른 사람은 어떻게 생각하는지 궁금하다.
컨트롤러의 코드다. view에 menus를 전달해서 화면을 그리고, 사용자의 입력을 받는다. 그 결과에 따라 모델의 비즈니스 로직을 실행하고 다음 상태로 변하는 구조다. 내 생각에는 적절하게 책임을 나눈 것 같은데, 지금 보니까 일단 if else를 좀 없애고 싶다 ㅋㅋ 고민 해결되면 리팩토링 연습할 때 수정해야지.
@Override
public void handleInteraction(KioskController kc) {
List<Menu> menus = new ArrayList<>(kc.getKiosk().getBasket());
menus.addAll(model.getMenus());
view.print(menus);
System.out.print(INPUT_TEXT);
int userInput = UserInput.userInput(model.getMenus().size());
Menu menu = model.getMenus().get(userInput);
if (menu.getName().equals("주문")) {
kc.getKiosk().buy();
kc.setNextState(new OrderFinishState());
} else if (menu.getName().equals("메뉴판")) {
kc.setNextState(new MainState());
}
}
변경되지 않는 값 관리
메뉴판에 대한 정보를 각각의 모델 클래스를 만들어서 관리했는데, 메뉴판은 변경되는 상황이 없다보니 비즈니스 로직이 없는 DTO 다. 그래서 각각의 모델 클래스가 가지고 있는 컬렉션을 생성하는 팩토리 클래스를 만들고, 그 클래스의 메소드로 메뉴판 컬렉션을 생성하는 구조로 변경할지 고민했는데, 메뉴판이 변경될 때마다 생성 메소드를 추가, 수정하는 것보다는 메뉴판과 관련된 클래스만 관리하는게 더 괜찮은 것 같다고 생각했다. 다른 더 좋은 방법이 있을 것 같은데 아직 못 찾았다..
// 메뉴 모델 추상 클래스
public abstract class AbstractMenuModel<T> {
protected T menus;
public T getMenus() {
return menus;
}
}
public class MainMenuModel extends AbstractMenuModel<List<Menu>>{
public MainMenuModel() {
menus = Arrays.asList(
new Menu("Burgers", "앵거스 비프 통살을 다져만든 버거", MenuType.CATEGORY),
new Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림", MenuType.CATEGORY),
new Menu("Drinks", "매장에서 직접 만드는 음료", MenuType.CATEGORY),
new Menu("Beer", "뉴욕 브루클린 브루어리에서 양조한 맥주", MenuType.CATEGORY),
new Menu("Order", "장바구니를 확인 후 주문합니다.", MenuType.ORDER),
new Menu("Cancel", "진행중인 주문을 취소합니다.", MenuType.ORDER),
new Menu("Quit", "프로그램을 종료합니다.", MenuType.ORDER)
);
}
}
해결
나름대로 고민하면서 해결책이라고 생각한 상태 패턴을 적용하고 모델, 뷰, 컨트롤러의 책임을 적절히 나눴다고 생각했는데.. OOP 고수분들이 보시기엔 어떤지 궁금하다. 더 좋은 방법이 뭘까? 알게 되는대로 업데이트 해야지.
2023.10.20. 튜터님께 질문하면서 고민에 대한 답을 어느정도 얻었다.
상태 관리
중요한 것은 클래스 추가/변경에 따라 비즈니스 로직이 변하지 않는 것이다. 클래스가 많아지는 것은 큰 단점이 아니다. 다른 프로젝트를 보면 엄청난 수의 클래스가 있는 것을 알 수 있다. 물론, 불필요한 클래스를 생성하는 것은 지양해야지.
적절한 책임과 역할
지금으로썬 적절하게 나눈 것 같다. 더 나은 방법을 알게 되면 업데이트 해야지.
변경되지 않는 값 관리
클래스로 구분해서 관리하거나, 하나의 클래스에서 생성자 메소드를 만들거나 취향 차이라고 생각한다고 하신다. 듣고 나서 혼자서 조금 더 생각했는데, 클래스 이름의 접미사를 Model로 하지 말아야 했다. 메뉴판 데이터 클래스인걸 직관적으로 알 수 있게 바꾸는게 좋겠다.
'Tool > Java' 카테고리의 다른 글
[JPA] MultipleBagFetchException 이해하기 (1) | 2024.01.04 |
---|---|
[Spring] JPA 복합 키 사용하기 (0) | 2023.12.20 |
[Java] 리플렉션 API 간단 정리 (0) | 2023.10.23 |
[Java] Stream API 기초 정리 (0) | 2022.12.09 |