교수님이 쓰라고 하셔서 쓰는 그런데 은근 재밌는 듯

JAVA

[JAVA] 커피메이커 Ver.1

shinyunha 2025. 10. 1. 14:49

커피 만들기는 CoffeeMaker -> Coffee -> CoffeeMachine 흐름을 따라 진행된다.


CoffeeMaker

-Coffee를 소유(Has-A)한다. CoffeeMaker와 Coffee는 Association 관계이다.

-setCoffee() 메소드를 통해 coffee를 주입 받는다.

-makeCoffee() 메소드는 주입받은 coffee의 prepare() 메소드를 호출한다. 

public class CoffeeMaker {
	Coffee coffee;

    public void setCoffee(Coffee coffee){
    	this.coffee = coffee; //setter로 주입
    }

    public void makeCoffee(){
    	System.out.println(coffee.prepare());
    }

}

DI(의존성 주입)와 IoC(제어의 역전)를 위한 CoffeeMaker

 

주입의 3가지 방법!

1. 생성자를 이용한 주입 (권장)

2. Setter로 구현한 주입

3. 필드에 직접 주입


Coffee

-Coffee는 Data중심이 아니고 행동중심이기 때문에 클래스가 아니라 interface로 구현한다. 

-prepare() 메소드에 레시피를 담고 있기 때문에 행동중심이다. 

-Coffee들은 CoffeeMachine 소유(Has-A)한다. Coffee와 CoffeeMachin은 Association 관계이다.

-생성자를 통해 machine을 주입 받는다.

-prepare() 메소드는 주입 받은 machine의 brew() 메소드를 호출한다. 

public interface Coffee {

    public String prepare();

}

public class Americano implements Coffee {
    private CoffeeMachine machine;
    
    public Americano(CoffeeMachine machine) {
    	this.machine = machine;
    }

    public String prepare(){
        return machine.brew() + "물 붓기 ";
    }

}

public class Espresso implements Coffee {
    private CoffeeMachine machine;
    
    public Espresso(CoffeeMachine machine) {
    	this.machine = machine;
    }

    public String prepare(){
        return machine.brew();
    }

}

public class Latte implements Coffee {
	private CoffeeMachine machine;
	private MilkFother milkFother;
	
	public Latte(CoffeeMachine machine, MilkFother milkFother) {
    	this.machine = machine;
    	this.milkFother = milkFother;
    }

    public String prepare(){
        return machine.brew() + milkFother.frotherMilk();
    }

}

CoffeeMachine

-드디어 커피를 내린다

public interface CoffeeMachine {

    public String brew();

}

public class DripCoffeeMachine implements CoffeeMachine {

    public String brew(){
        return "드립 내리기 ";
    }

}

public class EspressoMachine implements CoffeeMachine {

    public String brew(){
        return "에스프레소 추출 ";
    }

}

public class MilkFother {

    public String frotherMilk(){
        return "우유 스팀 치기 ";
    }

}

혼자 둥둥 떠있는 외로운 MilkFrother (CoffeeMachine에는 속할 수 없다)

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//기계 만들기
		CoffeeMachine espressoMachine = new EspressoMachine();
		CoffeeMachine dripCoffeeMachine = new DripCoffeeMachine();
		MilkFother milkFother = new MilkFother();
		
		//에스프레소 만들기
		Espresso espresso = new Espresso(espressoMachine);
		System.out.println(espresso.prepare()); 
		
		//아메리카노 만들기
		Americano americano = new Americano(dripCoffeeMachine);
		System.out.println(americano.prepare()); 
		
		//라떼 만들기
		Latte latte = new Latte(espressoMachine, milkFother);
		System.out.println(latte.prepare());
		
		//주입 방식으로 라떼 만들기
		CoffeeMaker yunha = new CoffeeMaker();
		yunha.setCoffee(new Latte(dripCoffeeMachine, milkFother));
		yunha.makeCoffee();
	}

}

테스트 코드


커피메이커로 알아보는 S.O.L.I.D 

Single Responsibility: 단일 책임의 원칙

CoffeeMaker, Coffee, CoffeeMachine은 모두 커피를 만들기만 하고 주문을 받지는 않습니다. 만약 주문을 받고 싶다면 새로운 클래스를 생성해야합니다.

 

Open/Closed: 개방/폐쇄 원칙

만약 아인슈페너를 Coffee에 추가하고 싶다면 Coffee interface 구현을 통해 손쉽게 추가할 수 있습니다. 그리고 아인슈페너를 추가했다고 해서 다른 코드들이 망가지지 않습니다.

 

Liscov Subsitution: 리스코프 치환 원칙

MilkFrother는 CoffeeMachine과 관계를 가지지 않습니다. Coffee를 brew한다는 상위 타입의 특성을 따르지 않기 때문입니다.

 

Interface Segregation: 인터페이스 분리 원칙

레시피를 담고 있는 Coffee와 brew하는 CoffeeMachine은 인터페이스가 분리되어 있습니다.

 

Dependency Inversion: 의존성 역전 원칙

CoffeeMaker가 Coffee들을 생성하여 레시피를 찾는 것이 아니라, coffee를 주입받아서 prepare() 메소드를 호출합니다. Coffee들이 CoffeeMachine들을 생성하여 brew하는 것이 아니라 machine을 주입받아서 brew() 메소드를 호출합니다. 

 

 

 

 

 

 

 

 

 

 

'JAVA' 카테고리의 다른 글

[JAVA] 디자인 패턴 - 템플릿 메소드  (0) 2025.10.17
[JAVA] 디자인 패턴 - 전략 (짱구 예제)  (0) 2025.10.15
[JAVA] Thread  (0) 2025.09.24
[JAVA] Generic & Collection  (0) 2025.09.19
[Refactoring] Battle_상속,모듈화 문제 (JAVA)  (0) 2025.09.16