실제 모델 : 콤비 커피머신

커피를 어떻게 만드는지는 모르고 커피를 brew()하는 역할만 하는 것 → 인터페이스 사용
❋ MilkFrother 클래스도 CoffeeMachine을 구현하게 할까? → brew() 기능이 필요 없으니까 X... → 이 기능은 별도의 장치가 수행하는 역할이라고 생각해서 독립된 하나의 클래스로 생성하게 되었다 (CoffeeMachine은 커피를 brew()하는 역할만!)
음료는 스스로 제조 방법을 알고 있고 prepare()을 각자 구현하도록 함 → 인터페이스 사용
❋ Coffee는 인터페이스가 좋을까? 추상클래스가 좋을까? Coffee를 행동 중심으로 볼 것이라면 인터페이스, 데이터를 중심으로 본다면 추상클래스가 적합하다
▷ 행동 중심 : 커피가 스스로 제조방법을 알고 있어서 커피를 스스로 준비한다 → Coffee를 준비하는 행동의 책임을 가진다고 판단, 각 음료가 준비라는 행동을 구현하면 되는 것
▷ 데이터 중심 : 커피 원두 및 재료를 준비하고 바리스타가 만든다 → Coffee는 커피 원두 및 재료 같은 필드를 통해 필요한 데이터를 가지고 있는 것이고, 제조하는 책임은 바리스타에게 넘기는 것
음료를 주문하고 생성된 결과를 얻는 음료 주문 제작기 역할
특정 음료를 만드는 것이 아니라 Coffee 역할을 수행하는 모든 객체를 받아서 prepare()을 호출할 수 있음
Coffee 객체를 직접 생성하는 것 X → setter 방식으로 외부에서 전달 받는 것
적용된 원칙
① SRP : 커피 제조 과정에서 역할을 음료 준비, 커피 추출, 우유 거품 생성 등으로 분리하여 각 클래스가 하나의 책임을 담당하고 있다
② OCP : Coffee, CoffeeMachine 인터페이스를 이용하여 새로운 음료나 장비가 추가되더라도 기존 코드를 변경할 필요가 없다
③ LSP : Coffee와 CoffeeMachine의 각 구현체를 해당 인터페이스 타입으로 대체하여도 정상적으로 동작한다
④ ISP : 음료 준비와 커피 추출 역할을 Coffee, CoffeeMachine으로 분리하여 객체가 필요하지 않은 기능에 의존하지 않도록 하였다
⑤ DIP : CoffeeMaker와 각 음료들이 구체 클래스가 아니라 Coffee, CoffeeMachine인터페이스에 의존하며, 필요한 구현 객체는 외부에서 주입받도록 하였다
✴︎ IoC / DI : CoffeeMaker와 각 음료들이 필요한 객체를 직접 생성하지 않고 객체의 생성이나 선택에 대한 제어권을 외부(Main)에 넘기고 있다 / 필요한 객체를 외부에서 setter 방식 또는 생성자 방식을 통해 주입하고 있다
⇒ 결국에는 전부 연결되는 개념임을 코드를 통해 인식할 수 있다!


❋ 왜 여기서 CoffeeMachine 필드 앞에 private, final을 붙인 건가? → 알다시피 private은 CoffeeMachine을 외부에서 접근하지 못하도록 캡슐화하는 거고 final은 생성자를 통해 주입받은 CoffeeMachine이 이후에 다른 객체로 변경되지 못하게 만드는 거다 → 그냥 결론적으로는 둘다 코드가 이상하게 작동하지 않도록 하고 설계 의도를 명확하게 하려고 쓰는 것

다시 정리하자면
→ CoffeeMaker 클래스는 Coffee 인터페이스 타입의 필드를 가지고 있고 setter를 통해서 Coffee를 구현한 객체를 받을 수 있다
→ makeCoffee 메소드를 통해서 prepare()을 호출함으로써 Coffee 타입의 객체들을 전부 다 만들 수가 있다
⇒ 이러면 구체적인 객체에 대한 의존성을 낮추어 지금 의존하고 있는 객체를 바꿀 때 편리하다

→ EspressoMachine, DripCoffeeMachine, milkFrother 객체 생성한다
→ Espresso, Americano, Latte 객체 생성, 이때 생성자 주입을 통해 외부에서 생성된 객체를 넣어주고 있다
→ 생성된 객체에 직접 prepare()을 호출하면 각 음료가 생성된다
→ CoffeeMaker을 사용하면 setter 주입을 통해 외부에서 생성된 객체를 넣을 수 있고 makeCoffee()를 통해 해당 객체의 prepare()을 하여 음료를 생성한다
실행시켜보면 ? 에스프레소 아메리카노 라떼 전부 다 잘 나오는 걸 볼 수가 있다^..^
이번 실습을 통해서 SOLID를 적용하려고 하면서 인터페이스나 클래스를 어떻게 만들면 좋을지에 대해서도 배울 수 있었다
인터페이스와 추상클래스를 사용하는 상황에 대한 판단이나, 인터페이스를 어떤 기준으로 분리하면 좋을지에 대한 생각을 해보기도 했다
특히 IoC, DI는 어떻게 코드에서 사용되는지를 보면서 setter주입이나 생성자 주입을 왜 사용하는지에 대해서도 알게 되었다
전체적으로 인터페이스나 클래스들의 관계에 대해서 많이 생각해보게 되었던 것 같다
어떤 식으로 연결이 되어있어야 하는지 상속, 구현, 의존 이런 개념들을 다시 한 번 정리해보는 시간을 가질 수 있었다
역시 백문이 불여일습 ! ! !