목차
- 07 함께 모으기
- 커피 전문점 도메인
- 커피 주문
- 커피 전문점이라는 세상
- 설계하고 구현하기
- 커피를 주문하기 위한 협력 찾기
- 인터페이스 정리하기
- 구현하기
- 코드의 세 가지 관점
- 코드는 세 가지 관점을 모두 제공해야 한다
- 도메인 개념을 참조하는 이유
- 인터페이스와 구현을 분리하라
- 추상화 기법
- 커피 전문점 도메인
07 함께 모으기
서문
- 객체지향에는 개념, 명세, 구현의 세 가지 관점이 있다.
- 각 관점은 클래스를 바라보는 방식에 관한 것이며, 개발 순서에 따른 것이 아니다.
- 클래스는 아래의 세 가지 관점을 모두 수용하고, 명확하게 분리할 수 있어야 한다.
- 개념 관점은 도메인 내부의 각 개념들 간 관계에 관한 것이다.
- 도메인은 사용자들이 관심을 가지는 특정 분야나 주제를 뜻한다.
- 소프트웨어의 목적은 현실 도메인에 존재하는 문제를 해결(비즈니스 로직)하는 것이다.
- 도메인은 사용자들이 관심을 가지는 특정 분야나 주제를 뜻한다.
- 명세 관점은 소프트웨어에 관한 것이다.
- 명세 관점은 실제 소프트웨어에 존재하는 객체의 책임(인터페이스)에 초점을 맞춘다.
- 인터페이스와 구현을 분리하고, 인터페이스의 책임을 구성하는 것에 집중한다.
- 구현 관점은 객체들의 책임 수행에 필요한 코드에 관한 것이다.
- 각 객체가 책임을 어떻게 수행할 것인가에 집중한다.
커피 전문점 도메인
커피 주문
- 손님은 메뉴판을 통해 커피를 주문한다.
- 바리스타는 주문받은 커피를 제조한다.
커피 전문점이라는 세상
- 커피 전문점 도메인에는 손님 객체, 메뉴 항목 객체, 메뉴판 객체, 바리스타 객체, 커피 객체가 존재한다.
- 메뉴판 객체에는 메뉴 항목 객체인 아메리카노, 카푸치노, 카라멜 마키아또, 에시프레소가 속해 있다.
- 손님 객체는 메뉴판 객체를 통해 바리스타 객체에게 커피 객체를 주문한다.
- 바리스타 객체는 커피 객체를 제조한다.
- 요구사항 분석
- 손님은 메뉴판에서 주문할 커피를 선택할 수 있어야 한다.
- 손님은 메뉴판을 알아야 한다. 즉, 두 객체 사이에 관계가 존재해야 한다.
- 손님은 바리스타에게 주문을 할 수 있어야 한다.
- 두 객체 사이에 관계가 존재해야 한다.
- 바리스타는 커피를 제조할 수 있어야 한다.
- 두 객체 사이에 관계가 존재해야 한다.
- 손님은 메뉴판에서 주문할 커피를 선택할 수 있어야 한다.
- 인간이 복잡한 객체를 다루기 위해서는 동적인 객체를 정적인 타입으로 추상화해야 한다.
- 동일한 행동을 가진 객체는 동일한 타입의 인스턴스로 분류할 수 있다.
- 예) 메뉴판 객체는 메뉴판 타입의 인스턴스이다.
- 예) 아메리카노와 에스프레소 객체는 커피 타입의 인스턴스이다.
- 동일한 행동을 가진 객체는 동일한 타입의 인스턴스로 분류할 수 있다.
- Use Case 모델링
- 손님이 카페에 들어와 메뉴판을 확인한다.
- 손님은 선택한 커피를 바리스타에게 주문한다.
- 바리스타는 주문을 받고 커피를 제조한다.
- 바리스타는 제조된 커피를 손님에게 제공한다.
- 도메인 모델링
- 도메인 모델링은 소프트웨어가 대상으로 하는 도메인 영역을 단순화하는 작업이다.
- 도메인 모델링을 통해 객체 간 합성 관계를 표현할 수 있다.
- 메뉴판 객체는 다수의 메뉴 항목 객체를 포함한다.
- 이는 객체 간 포함 관계로 표현할 수 있다.
- 손님 타입은 메뉴판 타입을 알고 있으며, 서로 포함하지 않는다.
- 이는 객체 간 연관 관계로 표현할 수 있다.
- 메뉴판 객체는 다수의 메뉴 항목 객체를 포함한다.
설계하고 구현하기
커피를 주문하기 위한 협력 찾기
- 협력 설계에서는 먼저 메시지를 선택하고, 메시지에 적합한 객체를 선택해야 한다(개념 관점).
- 객체가 메시지를 처리할 책임을 맡게 되면, 해당 메시지는 객체의 공용 인터페이스에 추가된다.
- 커뮤니케이션 다이어그램 모델링
- 첫 번째 메시지: 커피를 주문하라(아메리카노)
- 책임을 수행하기에 적합한 타입을 찾는다. —> 손님 타입
- 책임을 수행할 객체를 해당 타입의 인스턴스로 만든다. —> 손님 객체
- 현실 객체와 유사한 이름을 붙인다. —> 손님
- 손님이 할 수 없는 일을 찾는다. —> 메뉴 항목을 모른다.
- 할 수 없는 일을 다른 객체에게 위임한다. —> 메뉴판 객체
- 두 번째 메시지: 메뉴 항목을 찾아라(메뉴 이름)
- 책임을 수행하기에 적합한 타입을 찾는다. —> 메뉴판 타입
- 책임을 수행할 객체를 해당 타입의 인스턴스로 만든다. —> 메뉴판 객체
- 현실 객체와 유사한 이름을 붙인다. —> 메뉴판
- 메뉴판이 할 수 없는 일을 찾는다. —> 없음
- 메뉴 항목을 찾는다.
- 메뉴 항목을 반환한다.
- 세 번째 메시지: 커피를 제조하라(메뉴 항목)
- 책임을 수행하기에 적합한 타입을 찾는다. —> 바리스타 타입
- 책임을 수행할 객체를 해당 타입의 인스턴스로 만든다. —> 바리스타 객체
- 현실 객체와 유사한 이름을 붙인다. —> 바리스타
- 바리스타가 할 수 없는 일을 찾는다. —> 커피 객체 생성
- 네 번째 메시지: 생성하라(메뉴 항목)
- 책임을 수행하기에 적합한 타입을 찾는다. —> 커피 타입
- 책임을 수행할 객체를 해당 타입의 인스턴스로 만든다. —> 커피 객체
- 현실 객체와 유사한 이름을 붙인다. —> 커피
- 커피가 할 수 없는 일을 찾는다. —> 없음
- 아메리카노의 이름과 가격을 초기화한다.
- 아메리카노를 반환한다.
- 아메리카노를 제조한다.
- 아메리카노를 반환한다.
- 첫 번째 메시지: 커피를 주문하라(아메리카노)
인터페이스 정리하기
- 각 객체가 수신 가능한 메시지를 토대로 인터페이스를 구성한다(명세 관점).
- 손님 객체: 커피를 주문하라
class Customer { public void order(String menuName) {} }
- 메뉴판 객체: 메뉴 항목을 찾아라
class MenuItem {} class Menu { public MenuItem choose(String name) {} }
- 바리스타 객체: 커피를 제조하라
class Barista { public Coffee makeCoffee(MenuItem menuItem) {} }
- 커피 객체: 생성하라
class Coffee { public Coffee(MenuItem menuItem) {} }
구현하기
- 인터페이스를 메소드로 구현한다(구현 관점).
- 커피를 주문하라
class Customer {
public void order(String menuName, Menu menu, Barista barista) {
MenuItem menuItem = menu.choose(menuName);
Coffee coffee = barista.makeCoffee(menuItem);
}
}
- 메뉴 항목을 찾아라
class Menu {
private List<MenuItem> items;
public Menu(List<MenuItem> items) {
this.items = items;
}
public MenuItem choose(String name) {
for(MenuItem each : items) {
if(each.getName().equals(name)) {
return each;
}
}
return null;
}
}
- 커피를 제조하라
class Barista {
public Coffee makeCoffee(MenuItem menuItem) {
Coffee coffee = new Coffee(menuItem);
return coffee;
}
}
- 생성하라
class Coffee {
private String name;
private int price;
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;
}
public int cost() {
return price;
}
public String getName() {
return name;
}
}
- 클래스 다이어그램 모델링
코드와 세 가지 관점
코드는 세 가지 관점을 모두 제공해야 한다
- 개념 관점
- 도메인을 구성하는 개념과 관계를 반영한다.
- 예) Customer, Menu, MenuItem, Barista, Coffee
- 현실의 도메인을 최대한 반영하여 변경과 유지보수를 편리하게 해 준다.
- 도메인을 구성하는 개념과 관계를 반영한다.
- 명세 관점
- 공용 인터페이스에 초점을 맞춘다.
- 인터페이스를 통해 구현과 관련된 세부 사항이 드러나지 않게 함으로써, 변화에 대한 안정성을 제고한다.
- 구현 관점
- 클래스의 내부 구현에 초점을 맞춘다.
- 메소드와 속성이 되도록 외부에 영향을 미치지 않도록 캡슐화한다.
- 클래스의 내부 구현에 초점을 맞춘다.
도메인 개념을 참조하는 이유
- 메시지 수신 객체를 선택하는 방법 중 하나는 도메인 개념 중 적합한 요소를 선택하는 것이다.
- 인간에게 익숙한 도메인에 대한 지식을 기반으로 코드를 쉽게 이해할 수 있게 해 줌으로써 유지보수성을 향상시킨다.
인터페이스와 구현을 분리하라
- 명세 관점과 구현 관점을 혼동하지 말아야 한다.
- 명세 관점은 클래스의 안정적인 측면에 집중한다. 설계의 품질을 결정한다.
- 구현 관점은 클래스의 불안정한 측면에 집중한다. 코드의 품질을 결정한다.
- 명세 관점의 인터페이스가 구현 관점의 세부사항을 노출해서는 안 된다.
'Study' 카테고리의 다른 글
개발지식 스터디 발표 자료(빌더 패턴 & 팩토리 메서드 패턴) (0) | 2023.07.18 |
---|---|
객체지향의 사실과 오해 부록A 발표 자료 (2) | 2023.07.12 |
객체지향의 사실과 오해 스터디 5장 발표 자료 (0) | 2023.07.03 |
커뮤니케이션 스터디 발표 자료(재귀함수) (0) | 2023.06.23 |
객체지향의 사실과 오해 스터디 2장 발표 자료 (0) | 2023.06.20 |