☕️ 김영한의 실전 자바 - 중급 1편 을 수강하며 학습한 내용을 저만의 언어로 정리하고 있습니다.
개발자 코뉴는 취업이 되지 않아서 아이스크림 가게를 차렸다. 기이한 취향으로 인해 맛은 바닐라랑 민트초코랑 와사비밖에 없다. 🤤
전공 지식을 살려 키오스크에 탑재될 주문 시스템을 본인이 코딩하기로 한다.
왜인지 모르겠지만 손님이 키오스크에서 직접 주문할 맛을 문자로 입력하고 전송하게 했다.
IceCreamOrderService
public class IceCreamOrderService {
public String orderMessage(String flavor) {
if (flavor.equals("VANILLA")) {
return "무난한 맛";
} else if (flavor.equals("MINT_CHOCO")) {
return "최고의 맛";
} else if (flavor.equals("WASABI")) {
return "이달의 맛";
} return "주문 오류";
}
public int orderPrice(String flavor) {
int price = 0;
if (flavor.equals("VANILLA")) {
return 3000;
} else if (flavor.equals("MINT_CHOCO")) {
return 5000;
} else if (flavor.equals("WASABI")) {
return 4000;
} return price;
}
}
IceCreamMain
public class IceCreamMain {
public static void main(String[] args) {
// VANILLA, MINT_CHOCO, WASABI 아이스크림을 파는 가게가 있다.
// 아이스크림을 주문해보자
IceCreamOrderService orderService = new IceCreamOrderService();
String message = orderService.orderMessage("MINT_CHOCO");
int price = orderService.orderPrice("MINT_CHOCO");
System.out.println("price = " + price + " message = " + message);
}
}
price = 5000 message = 최고의 맛
이 나온다. "VANILLA", "WASABI"의 경우도 원하는 결과를 잘 반환한다.만약 손님이 "MINT_CHOCO" 대신 "TOOTHPASTE"를 입력한다면?
아니면 "MINT_CHOCO" 대신 "mint choco"를 입력한다면?
String message = orderService.orderMessage("TOOTHPASTE");
int price = orderService.orderPrice("TOOTHPASTE");
System.out.println("price = " + price + " message = " + message);
price = 0 message = 주문 오류
를 뱉으며 정상적으로 주문이 되지 않을 것이다.VANILLA, MINT_CHOCO, WASABI
를 입력할거라고 보장할 수 없다.위에서 만든 IceCreamOrderService
의 두 메서드 orderMessage(String flavor), orderPrice(String flavor)
는 아이스크림의 맛을 문자열로 받아 주문을 처리한다.
이로 인해 오타, 혹은 다른 문자열을 입력해서 주문했을 때 주문 결과에 문제가 있었다.
문자열을 사용하는 현재 방식은 타입 안전성에 문제가 있다.
값 제한 부족
주문이 가능한 "VANILLA", "MINT_CHOCO", "WASABI"
문자열만 받아야 하나,
오타 "VANILA", "MINT-CHOCO", "wasabi"
등이 들어와도 일단 orderService의 메서드에 넘길 수 있다.
"mint choco"
나 "mint-choco"
나 "MINT_CHOCO"
나 다 같은 결과를 내지 않을까? 라는 생각을 할 수도 있다.아예 없는 값인 "chocolate", "TOOTHPASTE"
등도 일단 orderService의 메서드에 넘길 수 있다.
컴파일 시 오류 감지 불가
개발자가 나열(enumeration)한 상수들을 정의하고, 이 상수들만 사용할 수 있도록 하는 패턴이다.
enum
이 자바에 공식적으로 도입되기 전 사용하던 패턴이다.⭐️ IceCreamFlavor
public class IceCreamFlavor {
// 아이스크림 맛을 상수로 선언한다.
public static final IceCreamFlavor VANILLA = new IceCreamFlavor();
public static final IceCreamFlavor MINT_CHOCO = new IceCreamFlavor();
public static final IceCreamFlavor WASABI = new IceCreamFlavor();
// 외부에서 임의로 인스턴스를 생성할 수 없도록 private 생성자를 만든다.
private IceCreamFlavor() {
}
}
사용할 아이스크림 맛들을 상수로 선언하고, 인스턴스를 생성한다.
외부에서 새로 인스턴스를 생성할 수 없게 생성자를 private
로 접근 제한한다.
IceCreamFlavor
상수들은 VANILLA, MINT_CHOCO, WASABI
로 제한된다.IceCreamFlavor.VANILLA, MINT_CHOCO, WASABI
는 항상 처음의 참조값을 유지한다. ==
비교가 가능해진다.IceCreamOrderService (수정)
public class IceCreamOrderService {
public String orderMessage(IceCreamFlavor flavor) {
if (flavor == IceCreamFlavor.VANILLA) {
return "무난한 맛";
} else if (flavor == IceCreamFlavor.MINT_CHOCO) {
return "최고의 맛";
} else if (flavor == IceCreamFlavor.WASABI) {
return "이달의 맛";
} return "주문 오류";
}
public int orderPrice(IceCreamFlavor flavor) {
int price = 0;
if (flavor == IceCreamFlavor.VANILLA) {
return 3000;
} else if (flavor == IceCreamFlavor.MINT_CHOCO) {
return 5000;
} else if (flavor == IceCreamFlavor.WASABI) {
return 4000;
} return price;
}
}
IceCreamMain (수정)
public class IceCreamMain {
public static void main(String[] args) {
// VANILLA, MINT_CHOCO, WASABI 아이스크림을 파는 가게가 있다.
// 아이스크림을 주문해보자
IceCreamOrderService orderService = new IceCreamOrderService();
String message = orderService.orderMessage(IceCreamFlavor.MINT_CHOCO);
int price = orderService.orderPrice(IceCreamFlavor.MINT_CHOCO);
System.out.println("price = " + price + " message = " + message);
}
}
위에서 설명한 타입 안전 열거형 패턴을 편리하게 사용할 수 있도록 자바 5부터 도입된 것이 Enum Type이다.
Enum type의 특징
⭐️IceCreamFlavor (enum으로 수정)
public enum IceCreamFlavor {
VANILLA, MINT_CHOCO, WASABI
}
더 나아가, enum type도 클래스이므로 기존에 IceCreamOrderService
에 속해있었던 메시지나 가격 관련 정보를 IceCreamFlavor
하나에 묶어서 보관할 수 있다!
⭐️IceCreamFlavor (리팩토링)
public enum IceCreamFlavor {
VANILLA("무난한 맛", 3000),
MINT_CHOCO("최고의 맛" ,5000),
WASABI("이달의 맛", 4000);
private final String message;
private final int price;
// private로 막혀있다고 생각하면 된다.
IceCreamFlavor(String message, int price) {
this.message = message;
this.price = price;
}
public String getMessage() {
return message;
}
public int getPrice() {
return price;
}
}
IceCreamOrderService
를 삭제해도 상관 없다.IceCreamOrderMain
import static enumeration.example.IceCreamFlavor.MINT_CHOCO;
public class IceCreamMain {
public static void main(String[] args) {
// VANILLA, MINT_CHOCO, WASABI 아이스크림을 파는 가게가 있다.
// 아이스크림을 주문해보자
String mintChocoMessage = MINT_CHOCO.getMessage();
int mintChocoPrice = MINT_CHOCO.getPrice();
System.out.println("price = " + mintChocoPrice + " message = " + mintChocoMessage);
// 전체 주문
System.out.println("\n전체 주문");
IceCreamFlavor[] flavors = IceCreamFlavor.values();
for (IceCreamFlavor flavor : flavors) {
System.out.println("price = " + flavor.getPrice() + " message = " + flavor.getMessage());
}
}
}
.values()
같은 enum 타입 메서드도 사용해서 IceCreamFlavor
에 있는 모든 값들을 꺼내올 수도 있다.enum type을 적용해서 이상한 아이스크림 주문 시스템을 개선한다면
이런 화면이 될 것이다.