만들 프로그램의 개요
필요한 객체 정의
커피와 티의 공통특성을 추상클래스인 Product로 추출
product.java
package checkpoint.products;
public abstract class Product {
// 커피와 차의 공통 정보를 가지고 있어야 한다.
// 이름, 가격 + id (Primary Key)
private int id;
private String name;
private int price;
public abstract void applyOption();
public abstract String getOptiontoString();
}
id (Primary Key) : 중복되지 않는 번호, 아이디
coffee 클래스
public class Coffee extends Product {
private boolean additionalShot
public
// - 옵션 선택 내용을 문자열로 만들어주기
private void setAdditionalShot(boolean addtionalShot) {
this.additionalShot = additionalShot;
}
public void applyOption() {
System.out.println("샷을 추가하시겠습니까? (1)_예 (2)_아니오"
Scanner scanner = new Scanner(System.in);
int option = Interger.parseInt(scanner.nextline());
if (option ==1) setAdditionalShot(true);
}
@Override
public String getOptionToString() {
return addtionalShot ? "(샷 추가)" : "";
}
}
public class Tea extends Product {
private boolean noTeabag
// - 상품에 따라 옵션을 물어봐주고 입력 받기
// - 입력한 옵션을 적용해주기
// - 옵션 선택 내용을 문자열로 만들어주기
private void setNoTeabag(boolean noTeabag) {
this.noTeabag = noTeabag;
}
public void applyOption() {
System.out.println("티백을 빼드릴까요? (1)_예 (2)_아니오"
Scanner scanner = new Scanner(System.in);
int option = Interger.parseInt(scanner.nextline());
if (option ==1) setNoTeabag(true);
}
@Override
public String getOptionToString() {
return noTeabag ? "(티백 제거)" : "";
}
}
동일하게 생성자 부분 private로 수정
productrepository 클래스
// 상품들의 목록을 가지고 있고, 그 목록과 관련된 역할을 수행하는 객체
public class ProductRepository {
private Product[] products = new Product[] {
new Coffee(id:1, "Americano", 3000, false);
new Coffee(id:2, "Caffe latte", 3500, false);
new Tea(id:3, "Peppermint tea", 4000, false);
new Tea(id:3, "Rooibos tea", 4500, false);
};
public Product[] getProducts() {
return Product;
};
public Product findById(int productId) {
for (Product product : products) {
if (product.getId() == productId) return product;
}
return null;
}
}
public class Kiosk {
private ProductRepository productRepository = new productRepository();
public void operate() {
// 메뉴 출력
System.out.println("# 메뉴");
System.out.println("-".repeat(count:50));
for (Product product : oductRepository.getProducts() {
System.out.printf(
"(%d) %-15s %d\n",
product.getId(),
product.getName(),
product.getPrice());
};
System.out.println("-".repeat(count:50));
System.out.println("메뉴를 골라주세요.");
// 메뉴 고르게 하기
int selectId = Integer.parseInt(scanner.nextLine());
Product selectedProduct =
productRepository.findById(selectedId);
// 옵션 적용하기
// - 상품에 따라 옵션을 물어봐주고 입력 받기
// - 입력한 옵션을 적용해주기
// - 옵션 선택 내용을 문자열로 만들어주기
selectedProduct.applyOption();
// 할인 적용하기
// 주문 내역 출력하기
int price = selectedProduct.getPricd();
System.out.println("주문이 완료되었습니다. ")
System.out.printf(
"주문 상품 : %s %s\n",
selectedProduct.getName(),
selectedProduct.getOptionToString());
System.out.printf("가격 : %d\n", price);
}
}
public class Main {
public static void main(String[] args) {
Kiosk kiosk = new kiosk(new BEDiscountCondition(discountRate:20);
operate.Kiosk();
}
}
public void operate {
printMenu();
Product selectedProduct = chooseMenu();
selectedProduct applyOption();
order(selectedProduct);
}
public class CozDiscocuntCondition {
// 할인 금액을 저장하는 필드
private int discountAmount;
public CozDiscountCondition(int discountAmount) {
this.discountAmount= discountAmount;
}
// 할인 조건을 물어보는 메서드
private boolean checkDiscountContdition() {
Scanner scanner = new Scanner(System.in);
System.out.println("코드스테이츠 수강생이십니까? (1)_예 (2)_아니오");
String input = scanner.nextLine();
if (input.equals("1")) return true;
else return false
}
// 할인 금액을 계산해주는 메서드
private int cal
return price - discountAmount;
// 할인을 적용해주는 메서드
public int discount(int price) {
if (checkDiscountContdition()) return 할인 금액 계산해주는 메서드(price);
else return price;
}
}
요구사항 : 할인 조건은 추후에 자주 변동될 수 있습니다.
-> 백엔드 수강생한테만 20% 할인 (할인 대상, 할인 적용 방법 변경)
-> 새로운 BEDiscountCondition 클래스 생성
BeDiscountCondition 클래스
public class beDiscountCondition {
// 할인 금액을 저장하는 필드
private int discountAmount;
public beDiscountCondition(int beDiscountCondition) {
this.discountAmount= discountAmount;
}
// 할인 조건을 물어보는 메서드
private boolean checkDiscountContdition() {
Scanner scanner = new Scanner(System.in);
System.out.println("코드스테이츠 백엔드 수강생이십니까? (1)_예 (2)_아니오");
String input = scanner.nextLine();
if (input.equals("1")) return true;
else return false
}
// 할인 금액을 계산해주는 메서드
private int cal
return price - (price * discountRate);
// 할인을 적용해주는 메서드
public int discount(int price) {
if (checkDiscountContdition()) return 할인 금액 계산해주는 메서드(price);
else return price;
}
}
기존의 클래스 변경
-> Kiosk 클래스
-> cozDiscountCondition 에서 beDiscountCondition를 호출 변경?
-> 실무가면 수십 수백군데에서 바꿀경우가 생긴다
객체지향의 원리를 활용하자
-> Kiosk 클래스가 구체적인 클래스에 의존하고있다, 구현에 의존하고있다
-> discount를 어떤 클래스가 하는지 알지 못하게 해야한다
// xxxx.discount()
xxxx가 누군지 몰라도 discount() 메서드를 가지고 있다는 것을 보장
-> 추상클래스 or 인터페이스 사용
DiscountCondition 인터페이스
public interface DiscountCondition {
int discount(int price);
}
implements DiscountCondition 를 클래스 선언 옆에 추가
해당 코드도 인터페이스 타입으로 변경
int price = discountCondition.discount(selectedProduct.getPrice);
근본적인 문제는 해결되지 않았음
-> kiosk 클래스가 new라는 키워드를 사용해서 결정하는 것을 못하게해야함
Kiosk에서 해당부분 제거
private DiscountCondition discountContion;
public Kiosk(DiscountCondition discountContion) {
this.discountCondition = discountContion;
}
Kiosk kiosk = new kiosk(new BEDiscountCondition(discountRate:20);
이를 의존성 주입이라 한다
우리는 프로그램 로직에 해당하는 코드를 변경하지 않더라도 변경할 수 있게 하는게 목표
-> Main 메서드에서도 빼주면된다
-> 환경설정에 해당하는 클래스를 만들어주면 된다
AppConfigurer 클래스
-> 로직이 아닌 설정만 있는 클래스
public class AppConfigurer {
public DiscountCondition discountConditon() {
return new BEDiscountCondition(20);
}
}
AppConfigurer appConfigurer = new Appconfigurer
변경이 예상되는 코드를 DI해두면 관리의 효율성이 오르는 원리
DI는 스프링의 중요한 원리