커피 만들기는 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 |