230228 체크포인트 - 다듬기 전

허크·2023년 2월 28일
0
  • 만들 프로그램의 개요

  • 필요한 객체 정의

    1. 커피, 차 : Coffee, Tea -> Product
    2. 상품들의 목록 및 저장소 : ProductRepository
    3. 할인 조건 : CozDiscountCondition
    4. 프로그램의 메인 로직 : Kiosk
  • 커피와 티의 공통특성을 추상클래스인 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 ? "(샷 추가)" : "";
    }
}

    
  • 굳이 에디셔널샷 셋 생성자를 공개할이유가 없으므로 private로 수정
  • Tea 클래스
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;
    }
}
  • Product 배열참조함수로 배열로 담을 수 있음
  • Kiosk 클래스
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);
    }
}
  • 키오스크 클래스는 메인이 아니므로 스태틱없이 자유롭게 수정가능
  • main 클래스
public class Main {

	public static void main(String[] args) {
    	Kiosk kiosk = new kiosk(new BEDiscountCondition(discountRate:20);
    	operate.Kiosk();
    }
}
  • 리팩토링 : 코드를 간결하게 하는 것
    오른쪽클릭 -> refector -> extract Method
public void operate {
	printMenu();
    Product selectedProduct = chooseMenu();
    selectedProduct applyOption();
    order(selectedProduct);
}
  • 혹시 extract 한 클래스에 static 붙어있으면 제거해주기
  • CozDiscocuntCondition 클래스

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;
    }
    
}
  • 할인조건이 변경되더라도 외부에서 확인할수없게 private 사용

  • 요구사항 : 할인 조건은 추후에 자주 변동될 수 있습니다.
    -> 백엔드 수강생한테만 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;
}
  • Main에 결정하도록 수정
Kiosk kiosk = new kiosk(new BEDiscountCondition(discountRate:20);
  • 이를 의존성 주입이라 한다

  • 우리는 프로그램 로직에 해당하는 코드를 변경하지 않더라도 변경할 수 있게 하는게 목표
    -> Main 메서드에서도 빼주면된다
    -> 환경설정에 해당하는 클래스를 만들어주면 된다

  • AppConfigurer 클래스
    -> 로직이 아닌 설정만 있는 클래스

public class AppConfigurer {

	public DiscountCondition discountConditon() {
    	return new BEDiscountCondition(20);
    }
}
  • Main 클래스 변경

	AppConfigurer appConfigurer = new Appconfigurer
  • 변경이 예상되는 코드를 DI해두면 관리의 효율성이 오르는 원리

  • DI는 스프링의 중요한 원리

profile
codestates seb 44th // 다크모드로 보는걸 추천드립니다

0개의 댓글