객체지향 설계 안에 존재하는 세가지 상호 연관된 관점

1. 개념 관점 ( Conceptual Perpective )

- 개념과 개념들 사이의 관계


2. 명세 관점 ( Specification Perspective ) 인터페이스

- 객체들의 책임, 객체가 협력을 위해 무엇을 할 수 있는가


3. 구현 관점 ( Implementation perspective )

- 객체의 책임을 어떻게 수행 할 것인가.



클래스는 세 가지 관점을 모두 수용할 수 있도록 개념, 인터페이스, 구현을 함께 드러내야 한다.

동시에 코드 안에서 세가지 관점을 쉽게 식별할 수 있도록 분리해야한다.



커피전문점 예제의 목표

1. 도메인 모델에서 시작해서 최종 코드까지의 구현과정을 설명

2. 구현 클래스를 개념,명세,구현 관점에서 바라보는 것이 무엇을 의미하는지 설명



커피 전문점 도메인

Menu : 아메리카노, 카푸치노, 마끼아또, 에스프레소 (4개)


도메인

- 손님 객체

- 메뉴항목 객체

- 메뉴판 객체

- 바리스타 객체

- 커피 객체


객체들간의 관계

- 손님은 메뉴판을 알아야 함

- 손님은 바리스타에게 메뉴주문

- 바리스타는 커피를 제조


포함관계 또는 합성관계

메뉴항목 객체는 메뉴판 객체에 포함되어 있음


연관관계

손님은 메뉴판을 알아야하지만

메뉴판은 손님의 일부가 아니므로 포함관계는 아니다.

그냥 서로 알고 있어야할 경우가 연관관계



커피전문점의 도메인을 단순화시킨것



연관관계, 포함관계가 중요한것은 아니다. 

실제로는 각 객체들 사이에 관계가 존재한다는 사실을 이해하는 것이 중요하다. 


다음단계는 협력을 설계하는 것.

즉, 적절한 객체에게 적절한 책임을 할당




설계하고 구현하기

협력찾기

메시지를 먼저 선택하고, 메시지를 수신하기에 적절한 객체를 선택해야 한다.




1. 커피를 주문하라

- 어떤 객체가 커피를 주문할 책임이 있는가 = 손님

- 손님은 메뉴항목을 모른다


2. 메뉴 항목을 찾아라 

- 어떤 객체가 메뉴 항목을 반환할 수 있는가 = 메뉴판

- 메뉴항목을 얻었으니 커피를 제조해달라고 요청할 수 있다


3. 커피를 제조하라

- 바리스타는 커피제조법을 모두 알고 있다(자율적인 객체 : 어떻게 만들든 상관없지)



인터페이스 정리하기

객체가 수신한 메시지가 객체의 인터페이스를 결정한다.


객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션(외부에서 접근 가능한)이 존재한다.


- 손님객체 : 제품을 주문하라

- 메뉴판객체 : 메뉴 항목을 찾아라

- 바리스타 객체 : 커피를 제조하라

- 커피 객체 : 생성하라


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) {}
}





구현하기


/*
 *     1. Menu에게 menuName에 해당하는 MenuItem을 요청해야 함
 *     2. 이를 받아 Barista에게 커피만들라고 전달해야함
 *     3. 즉 Menu객체와 Barista 객체에 대한 참조를 알아야하므로 인자로 전달받는 방법을 선택
 */
class Customer {
    
    // Menu, Barista 객체를 인자로 받음
    public void order(String menuName, Menu menu, Barista barista) {
        
        // Menu에게 menuName에 해당하는 MenuItem을 요청 
        MenuItem menuItem = menu.choose(menuName);
        
        // 전달받은 MenuItem으로 Baraista에게 커피만들라고 요청
        Coffee coffee = barista.makeCoffee(menuItem);
 
        // ....
    }
}
 
/*
 *     1. menuName 에 해당하는 MenuItem을 찾아야 함
 *     2. 이를 위해 내부적으로 MenuItem을 관리
 */
class Menu {
    private List<MenuItem> items;
 
    public Menu(List<MenuItem> items) {
        this.items = items;
    }
 
    // 요청온 name으로 MenuItem에서 커피이름과 가격을 조회
    public MenuItem choose(String name) {
        for (MenuItem each : items) {
            if (each.getName().equals(name)) {
                return each;
            }
        }
        return null;
    }
}
 
 
/*
 * 커피를 만들어서 Coffee 리턴
 */
class Barista {
    
    public Coffee makeCoffee(MenuItem menuItem) {
        Coffee coffee = new Coffee(menuItem);
        return coffee;
    }
}
 
/*
 *     1. 자기 자신을 생성하기 위한 생성자를 제공
 *     2. Coffe는 커피이름과 가격을 가지고 생성자 안에서 MenuItem에 요청을 보내 
 *         커피 이름과 가격을 얻고 속성에 저장 
 */
class Coffee {
    private String name;
    private int price;
    
    public Coffee(MenuItem menuItem) {
        this.name = menuItem.getName();
        this.price = menuItem.getCost();
    }
}
 
 
/*
 *     메뉴 item
 */
class MenuItem{
    private String name;
    private int price;
    
    public MenuItem(String name, int price) {
        this.name = name;
        this.price = price;
    }
    
    public int getCost() {
        return this.price;
    }
    
    public String getName() {
        return this.name;
    }
}




< 책에서 작성한 클래스다이어 그램>






< 직접 작성한  클래스 다이어 그램 >




코드와 세가지 관점
코드는 세 가지 관점을 모두 제공해야 한다.

개념,명세,구현

개념관점
현실세계에서도 바리스타가 커피를 제조하듯
Barista라는 클래스가 커피를 제조할 것이라고 쉽게 유추가능

명세관점
클래서의 인터페이스를 바라본다.
public 메서드는 다른 클래스가 협력할 수 있는 공용 인터페이스.
변화의 안정적인 인터페이스를 만들기 위해 구현과 관련된 세부사항이 드러나지 않게 해야한다.

구현관점
클래스의 내부 구현을 바라본다
메서드의 구현과 속성의 변경은 원칙적으로 외부의 객체에게 영향을 미쳐서는 안된다.
메서드와 속성은 클래스 내부의 비밀.(캡슐화 되야 한다)

세가지 관점은 동일한 코드를 바라보는 서로 다른 관점이다.
세가지 관점을 모두 포함하면서도 각 관점에 대응되는 요소를
명확하고 깔끔하게 드러낼수 있어야 한다.











+ Recent posts