키오스크에서 각 상품별로 옵션 기능을 추가해줬다.
개인적으로 가장 복잡하게 느꼈던 기능이었다.
옵션 기능을 구현과정을 기록한다.
기존에는 상세 상품에서 상품을 선택하면 바로 장바구니에 담을지 묻는 화면으로 넘어갔다.
하지만 옵션 기능을 추가하면 상품을 선택할 시에 해당하는 상품의 옵션을 선택할지 물어보는 화면이 출력된다.
옵션을 선택하면 그제서야 장바구니에 담을지 물어보도록 하는 것이 요구사항이었다.
항상 요구사항을 명확히 이해하는 것에 신경썼다.
요구사항을 명확하게 이해는 했는데, 어떻게 구현해야할까..
먼저 내 코드의 흐름을 전체적으로 다시 살펴봤다.
메인 메뉴 -> 상세 상품 -> 상품 선택 -> 옵션 -> 옵션 선택 -> 장바구니 담을 것인가? -> Yes -> 장바구니에 담겨진다.
전체적으로 이러한 흐름이 필요했다.
상품을 선택하면 장바구니에 담을 것인지 묻는 화면으로 넘어가는게 아니라 옵션 화면으로 넘어가줘야 했다.
그래서 choiceOption이라는 메서드를 생성했다.
전체 Product에 적용해보는게 아닌, Drinks 클래스에 먼저 옵션 기능을 추가해봤다.
Drinks 클래스에 옵션 기능이 정상적으로 실행된다면 다른 product 클래스에 해당 로직을 구현하는건 그냥 복붙과도 같은 일이기 때문이다.
choiceOption에서 상세 상품의 정보를 화면에 출력하고
내가 각 상품에 걸어놓은 옵션 값을 화면에 출력해주는 것을 목표로 했다.
각 상품마다 옵션이 상이할 것이라는 전제 조건을 걸어서 난이도가 꽤 높았다.
기능 구현도 중요하지만, 실용성있는 코드를 작성해야한다.
만약 내 프로그램을 구매하는 사장이 각 상품에 옵션을 추가하려고 하는데 어떤 상품은
옵션을 2개를 놓고 어떤 상품은 옵션을 5개를 놓을 수도 있지 않은가?
그리고 각 옵션마다 해당 상품의 정보가 변경되어 장바구니가 담겨야 한다.
예를 들면 마라로드 새우버거에 피클을 추가하는 옵션을 선택한다면 기존에 5천원이었던 가격에서
5백원을 더한 5천5백원으로 변경되고, 버거명도 마라로드 새우버거(피클추가)로 변경되어 장바구니에 추가되어야 한다.
일단 HashMap을 사용해서 bugerArr에 담겨있는 인덱스 값들을 key 값으로 가지는 bugerMap 객체를 생성해줬다.
그리고 해당 key 값의 value로 Option 객체를 선언해줬는데, 이 부분도 굉장히 어려웠다.
처음에는 Option 부분에 담길 수 있는 String 타입의 변수들이 있고, 그 변수들을 초기화해주는 생성자를 여러개 오버로딩하려고 했었다.
// 옵션 객체
public class ProductOption {
public String bugerOption1;
public String bugerOption2;
public ProductOption(String bugerOption1, String bugerOption2){
this.bugerOption1 = bugerOption1;
this.bugerOption2 = bugerOption2;
}
}
일단 위처럼 Option 객체를 생성하면 다양한 문제가 있다.
첫번째. 각 Product 별로 옵션이 상이한데 옵션이 한개면 한개의 옵션만 초기화해주는 생성자가 필요하고
옵션이 두개면 두개의 옵션만 초기화해주는 생성자가 필요하다.
public class ProductOption {
public String bugerOption1;
public String bugerOption2;
public String bearOption1;
public String dessertOption1;
... 옵션 변수가 무수히 많아짐
public ProductOption(String bugerOption1){
this.bugerOption1 = bugerOption1;
}
public ProductOption(String bugerOption1, String bugerOption2){
this.bugerOption1 = bugerOption1;
this.bugerOption2 = bugerOption2;
}
public ProductOption(String bearOption1){
this.bearOption1 = bearOption1;
}
public ProductOption(String dessertOption1){
this.desserOption1 = dessertOption1;
}
..... 옵션 초기화 생성자가 무수히 많아짐
}
그리고 각 상품별로 Option이 존재하므로 생성자가 무수히 많아지게 된다.
따라서 개선점이 필요했다.
두번째. Option을 Product 내부에서 초기화할 때 해당 상품이 가지는 모든 옵션을 화면에 출력해줘야 하는데 Option 변수 값이 String 형이다 보니 길이를 구할 수 없었다.
따라서 해당 Option 값을 화면에 뿌려주려면 아래와 같이 선언해줬어야 했다.
ProductOption option = drinkMap.get(0);
sout("1. " + option.drinkOption1 + " ");
sout("2. " + option.drinkOption2 + " ");
이렇게 되면 각 상품에 존재하는 옵션을 모두 선언해줘야 하는데........
당연히 내가 원하는 방식이 아니다.
나는 확장가능성이 있고, 유지보수성이 좋은 코드를 짜야하는데 위처럼 구현할 바에는 그냥 구현하지 않는게 낫다.
ㅡ
오랜 시간 고민끝에 방법을 찾았다. 바로 메서드에 가변 매개변수를 받는 것이다.
아래는 그것을 적용한 예제이다.
private List<String> options;
public ProductOption(String... options){
this.options = Arrays.asList(options);
}
public List<String> getOptions(){
return options;
}
매개변수에 String... options로 각 상품마다 옵션의 갯수를 자유롭게 설정할 수 있게되었다.
따라서, 생성자를 단 한번만 초기화하면 된다. 그리고
넘겨받은 options 매개변수 값들은 List 클래스에서 제공하는 asList를 사용하여 모두 담아줄 수 있도록 했다.
이렇게 담겨져 있는 options 배열 값들을 Product 객체에서 getOptions를 통해 size 정보를 얻어갈 수 있다.
List<String> options = productOption.getOptions();
for (int i = 0; i < options.size(); i++){
System.out.print((i+1) + ". " + options.get(i) + " ");
}
이로써 각 상품별로 상이한 크기를 가지는 options들을 화면에 모두 출력할 수 있게 되었다.
ㅡ
화면에서 출력된 option들 중 사용자가 하나를 선택하면 그 해당하는 숫자를 setProductOption() 메서드에 넘겨준다.
setProductOption() 메서드는 넘겨받은 옵션 번호에 해당하는 상품의 정보를 선택한 옵션 정보로 set해주는 메서드이다.
// choiceOption()
setProductOption(choiceOption, choiceDetailMenu);
// setProductOption()
public void setProductOption(int choiceOption, int choiceDetailMenu){
int key = choiceDetailMenu - 1;
if(choiceDetailMenu == 1 && choiceOption == 2){
bearArr.set(key, new ProductMenu("생맥주(거품 가득 생맥주)", 4.0, "거품이 일품인 생맥주!"));
}else if(choiceDetailMenu == 2){
if(choiceOption == 1) {
bearArr.set(key, new ProductMenu("카스", 4.0, "마시면 머리까지 시원해지는 카스!"));
}else if(choiceOption == 2){
bearArr.set(key, new ProductMenu("테라", 4.0, "마시면 머리까지 시원해지는 테라!"));
}else if(choiceOption == 3){
bearArr.set(key, new ProductMenu("KGB", 6.0, "상큼한 맛과 톡쏘는 알코올 KGB!"));
}else{
bearArr.set(key, new ProductMenu("하이네켄", 4.5, "깊은 맛이 일품인 하이네켄!"));
}
}else{
System.out.println();
}
}
이로써 사용자가 입력한 Product의 옵션에 해당하는 값으로 Product 값을 set해준다.
사용자는 이 값을 장바구니에 담을 수 있다.
이렇게 하여 Option 기능을 구현했다.
각 상품별로 상이한 옵션 값을 넣을 수 있고, 그 옵션 값에 해당하는 값으로 set해줘서
변동된 정보로 장바구니에 담아줄 수 있게되었다.
상품에서 옵션 값을 변경하면 장바구니에 담을 때 해당 상품명도 변경된다.
나는 장바구니에 중복되는 값이 담길 때 상품 명으로 구분을 해주는데, 옵션을 선택하면 상품명도 변경되어서 원치 않는 중복 결과가 나올 일이 자동으로 사라졌다!!! 아싸!!
생각치도 못한 곳에서 이득을 봐버려서 내가 설계를 잘짰나..?라는 생각이 들기도 했다
개발하면서 자주 느끼는건데, "이게 왜 안돼?"랑, "이게 왜 돼?"이다.
너무 공감되어서 웃겨죽겠닼ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
개발하면서 내가 생각한대로 코드를 짜고 실행을 해보면 보통은 안될 가능성이 높다고 생각한다.
당연히 처음부터 에러가 안나고 기능 구현하기란 쉽지 않으니까
그런데 키오스크를 개발하면서 내가 생각한대로 코드를 짜고 실행했을 때 거의다 내가 생각한대로 작동되어서 너무 신기했다
"오.. 이게 되네?"
개발할 때 항상 개선의 여지를 생각하자
이번 키오스크를 만들면서 항상 개선할 점이 무엇이 있는지, 이 프로그램이 상품화되었을 때
어떠한 문제점이 있을 수 있을지에 대해서 끊임없이 생각했고, 확장 가능한, 유지보수성이 좋은 코드를 작성하려고 노력했다.
혼자서 코드를 짜다보니 내가 객체지향적으로 코드를 작성했는지 명확히 알 수가 없는 점이 아쉽지만.
개선의 여지를 찾고 코드를 리팩토링해가는 과정이 내 적성이 너무 잘맞는다.
뭔가 레고 성을 다듬고, 부품을 새걸로 교체하고, 더 깨끗하게 닦아주고... 그런 느낌?
내 머릿속에 있는 지식의 조각들을 하나의 프로그램이라는 덩어리로 만들고, 리팩토링하면서 그 덩어리가 조금씩 견고해가는 과정을 보면 아주 큰 재미를 느낀다.
그리고 팀원 분들이 코드를 잘짰다고 칭찬해주셨는데 기분이 너무 좋았다.
이제 곧 자바를 이용하여 팀 과제도 진행하게 될 것이고,
스프링을 이용하여 프로젝트도 만들텐데 너무 기대된다.
개발자는 너무 매력있는 직업이다.
계속해서 개선할 점이 있고, 개선해나가면서 완벽에 다가가는 과정에서 두근거림을 느낀다.
물론 부족한 점을 개선한다는건 내가 하기싫은 일을 해야하는 것이기도 하지만,
나의 부족한 능력들을 점점 개선해나가면서 일정한 궤도 이상에 오르면
"내가 이걸 해냈네, 결국 해냈네"라는 생각이 들면서 가슴이 벅차오르기도 한다.