북스터디 마지막 Chapter 7을 읽고 정리해보았다.
💡 이번 장의 주제는 3가지 관점을 함께 다루는 것
1. 예제를 통해 도메인 모델에서 시작해서 최종 코드까지의 구현 과정 설명
2. 구현 클래스를 개념 관점, 명세 관점, 구현 관점에서 바라보는 것이 무엇을 의미하는지 설명
(예제) 커피 전문점에서 커피를 주문하는 과정을 객체들의 협력 관계로 구현해보자.
→ 커피 전문점이라는 도메인은 손님 객체, 메뉴 항목 객체, 메뉴판 객체, 바리스타 객체, 커피 객체로 구성된 작은 세상

💡 이렇게 소프트웨어가 대상으로 하는 영역인 도메인을 단순화해서 표현한 모델을 도메인 모델이라고 함. 도메인 모델의 초점은 어떤 타입이 도메인을 구성하느냐와 타입들 사이에 어떤 관계가 존재하는지를 파악함으로써 도메인을 이해하는 것 !
현재 설계하고 있는 협력: 커피를 주문하는 것
첫번째 메시지: ‘커피를 주문하라’
- 메시지 위에 붙은 화살표는 메시지에 담아 전달될 부가적인 정보인 인자
- 아메리카노를 주문할 경우 → 커피를 주문하라(아메리카노)

- 첫번째 메시지를 수신할 객체: 손님 객체(손님 타입의 인스턴스)
- 이제, 손님 객체는 커피를 주문할 책임을 할당받음

손님 객체는 할당된 책임을 수행하는 도중 스스로 할 수 없는 일은 메시지를 전송해 다른 객체에게 도움을 요청 : ‘메뉴 항목을 찾아라’
‘메뉴 항목을 찾아라’ 메시지를 수신할 객체 : 메뉴판 객체
메뉴항목을 얻은 손님은 메뉴 항목에 맞는 커피를 제조해달라고 요청 : ‘커피를 제조하라’
‘커피를 제조하라’ 메시지를 수신할 객체 : 바리스타 객체
커피 주문을 위한 협력은 바리스타가 새로운 커피를 만드는 것으로 끝남

협력을 설계하여 얻어낸 것은 객체들의 인터페이스
객체가 수신한 메시지가 객체의 인터페이스를 결정함
객체가 어떤 메시지를 수신할 수 있다는 것은 그 객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션이 존재한다는 것을 의미

실제로 소프트웨어의 구현은 동적인 객체가 아닌 정적인 타입을 이용해 이루어지므로, 객체들을 포괄하는 타입을 정의한 후 식별된 오퍼레이션을 타입의 인터페이스에 추가해야함
클래스를 통해 객체 타입 구현
public class Customer {
public void order(String menuName, Menu menu, Barista barista) {
// 내용
}
}
class MenuItem {…}
class Menu {…}
class Barista {…}
class Coffee {…}
public class Customer {
public void order(String menuName, Menu menu, Barista barista) {
// order 인자값으로 Menu와 Barista 객체를 전달받아 참조
MenuItem menuItem = menu.choose(menuName);
//Menu에게 menuName에 해당되는 MenuItem을 찾아달라고 요청
Coffee coffee = barista.makeCoffee(menuItem);
//MenuItem을 Barista 객체에게 전달하여 커피를 제조해달라고 요청
}
}
public class Menu {
private List<MenuItem> items; //Menu는 MenuItem의 목록을 포함
public Menu(List<MenuItem> items) {
this.items = items;
}
public MenuItem choose(String name) {
//MenuItem의 목록을 하나씩 검사해가면서 이름이 동일한 MenuItem을 찾아 반환
for (MenuItem each : items) {
if (each.getName().equals(name)) {
return each;
}
}
return null;
}
}
public class Barista {
public Coffee makeCoffee(MenuItem menuItem) { //MenuItem을 전달받아 커피를 제조
Coffee coffee = new Coffee(menuItem);
return coffee;
}
}
public class Coffee {
private String name;
private int price;
//커피는 자기 자신을 생성하기 위한 생성자 제공
//MenuItem에게 요청을 보내 커피 이름과 가경을 얻은 후 Coffee 속성에 저장
public Coffee(MenuItem menuItem) {
this.name = menuItem.getName();
this.price = menuItem.cost();
}
}
public class MenuItem {
private String name;
private int price;
public MenuItem(String name, int price) {
this.name = name;
this.price = price;
}
//Coffee의 요청에 응답할 수 있도록 cost() getName() 메서드 구현
public int cost() {
return price;
}
public String getName() {
return name;
}
}
💡 하나의 클래스 안에 세 가지 관점을 모두 포함하면서도 각 관점에 대응되는 요소를 명확하고 깔끔하게 드러내는 것이 핵심