[책] 객체지향의 사실과 오해 - 3
객체지향의 사실과 오해를 읽고 정리한 글입니다.
6. 객체 지도
어떤 사람이 길을 물어본다면 첫번째 해결책은 '기능적이고 해결책 지향적인 접근법'이 있다. 직접 길을 알려주는 것이다. 단계별로 상세히 설명할 수 있지만, 재사용이 불가능하다. 두번째 해결책은 지도를 이용하는 것이다. '구조적이고 문제 지향적 접근법'이며, 길을 찾을 수 있는 구조를 제공한다.
전통적인 소프트웨어 개발 방법은 길을 알려주는 것과 유사하며, 객체지향 개발 방법은 기능을 종속시키는 지도의 방법과 유사하다.
설계의 두가지 측면 - 구조, 기능
설계의 목적은 나중 설계를 허용하기 위해, 그리고 변경에 소요되는 비용을 낮추기 위해서이다. 이 목적을 위해 구조, 기능 두 가지 측면을 고려한다.
- 구조 측면의 설계 : 제품의 형태가 어떠해야 하는지에 초점
- 기능 측면의 설계 : 제품이 사용자를 위해 무엇을 할 수 있는지에 초점
설계를 위한 재료 : 구조와 기능
설계를 위해서는 안정적인 재료인 구조와 불안정한 재료인 기능이 있다.
- 구조 : 사용자나 이해관계자들이 도메인에 관해 생각하는 개념과 개념간의 관계로 표현한다.
- 도메인 모델링 - 구조를 수집하고 표현하기 위한 기법이다.
- 기능 : 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템의 행위로 표현
- 유스케이스 모델링 - 기능을 수집하고 표현하기 위한 기법
안정적인 재료 : 구조
도메인 모델은 소프트웨어를 사용하는 이해관계자들이 바라보는 멘탈 모델이다. 사용자의 관점을 반영한 도메인의 모습을 이용하면 정적인 타입을 이용해 세상을 단순화할 수 있으며, 본질적인 모습을 담고 있어 유연하게 대처할 수 있다. 즉, 본질을 잘 담고있는 도메인은 안정적인 구조를 잡을 수 있고, 안정적인 소프트웨어 구현이 가능하다는 것이다.
도메인 모델
- 사용자가 프로그램을 사용하는 대상 분야를 도메인이라고 한다.
- 대상을 단순화해서 표현하며, 복잡성을 관리하기 위해 사용하는 기본적인 도구
불안정한 기능을 담는 안정적인 도메인 모델
- 도메인의 본질을 잘 이해한 사용자의 관점을 반영해 소프트웨어를 설계한다.
- 본질적인 것을 도메인 모델에 녹여 놓으면 변경이 적어진다.
불안정적인 재료 : 기능
변경에 유연한 소프트웨어를 만들기 위해서는 언제 바뀔지 모르는 불안정한 재료인 기능을 유스케이스로 정리하는 것이 좋다. 유스케이스로 정리한 기능들을 도메인 모델 기반의 책임으로 분배해야 한다.
유스케이스
- 사용자와 사용자의 목표를 수행할 시스템 간의 상호작용 관점에서 시스템을 바라볼 수 있다.
유스케이스의 특성
- 사용자와 시스템간의 상호작용을 일련의 이야기 흐름으로 표현
- 유스케이스 안에 포함돼 있는 상호작용의 흐름이 중요
- 내부 설계와 관련된 정보는 포함하지 않는다
재료 합치기 : 기능과 구조의 통합
도메인 모델, 유스케이스, 책임-주도 설계
도메인 모델은 구조를, 유스케이스는 협력의 출발점인 시스템의 책임을 제공한다. 그리고 책임주도 설계를 한다. 책임주도 설계는 기능을 객체간 협력 관계로 바라보게 한다. 따라서, 두 가지 기본 재료인 유스케이스와 도메인 모델을 통합한다. 설계의 흐름은 대략 이러하다.
- 요구사항 식별
- 도메인 모델 생성
- 소프트웨어 클래스에 메서드 추가
- 요구사항 충족시키기 위해 객체간 메시지 전송 정의
기능 변경을 흡수하는 안정적 구조
구조가 안정적이라고 언급해온 이유는 도메인 모델이 안정적이기 때문이다. 시스템의 기능이 변경되더라도 객체 간의 관계는 보통 일정하게 유지된다.
- 변경에 대한 파급효과를 최소화하고, 요구사항 변경에 유연하게 대응할 수 있는 시스템 구축하기 위해서는 적절한 디자인 패턴 이용이 필요
예시 - 이자 계산 기능 구현
책에서 이자 계산 기능 구현을 위 설명한 개념들에 빗대어 설명하는데, 도메인을 이해하여 클래스 다이어그램을 짜고, 디자인 패턴을 활용해 기능의 변경을 어떻게 유연하게 대처하는지 보여준다. (이부분은 책을 직접 보는 것이 좋겠다)
7. 함께 모으기
코드와 도메인 모델을 밀접하게 연관시키는 단계이다. 개념 관점, 명세 관점, 구현 관점을 통해 객체 지향 설계를 해본다. 커피 전문점 도메인을 통해 책 전체에서 설명한 개념을 적용해보며 지금까지 배운 개념을 되돌아볼 수 있었다.
개념관점 (Conceptual Perspective)
도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다.
명세관점 (Specification Perspective)
- 사용자의 영역인 도메인을 벗어나 개발자 영역인 소프트웨어로 초점이 옮겨진다.
- 소프트웨어 안에서 살아 숨쉬는 객체들의 책임에 초점을 맞추게 된다.
- 객체의 인터페이스를 바라보게 된다.
구현관점 (Implementation Perspective)
- 프로그래머는 객체의 책임을 어떻게 수행할 것인가에 초점을 맞춘다.
- 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가
커피 전문점 도메인
커피를 주문하는 과정을 객체들의 협력 관계로 구현해보자.
객체 구성하기
- 손님 객체, 메뉴판 객체, 바리스타 객체, 커피 객체로 구성한다.
- 동적인 객체를 정적인 타입으로 추상화해서 복잡성을 낮춰야한다.
설계하고 구현하기
- 도메인 모델 안에 책임을 수행하기에 적절한 타입이 존재하는지 살펴보자.
- 적절한 타입을 발견했다면, 책임을 수행할 객체를 그 타입의 인스턴스로 만들자.
인터페이스 정리하기
- 메시지가 객체를 선택
- 선택된 객체는 메시지를 자신의 인터페이스로 받아들인다
- 객체가 어떤 메시지를 수신할 수 있다는 것은 그 객체의 인터페이스 안에 메시지에 해당하는 오퍼레이션이 존재한다는 것을 의미
구현하기
- 오퍼레이션을 수행하는 방법을 메서드로 구현
- Customer가 어떻게 Menu 객체와 Barista 객체에 접근할 것인가이다.
- 설계를 간단히 끝내고, 구현하자. 그래야 정확한 밑그림이 보인다.
코드와 세 가지 관점
개념관점
- Customer, Menu, MenuItem, Barista, Coffee 클래스가 보인다.
- 소프트웨어 클래스와 도메인 클래스 사이의 간격이 좁으면 좁을수록 기능을 변경하기 위해 뒤적거려야하는 코드 양도 줄어든다.
명세관점
- 클래스의 public 메서드는 다른 클래스가 협력할 수 있는 공용 인터페이스를 드러낸다.
구현관점
- 클래스의 내부 구현을 바라본다.
- 메서드의 구현과 속성의 변경은 원칙적으로 외부의 객체에게 영향을 미쳐서는 안된다.
인터페이스와 구현을 분리하라
- 명세관점과 구현관점이 뒤섞여 어지럽히지 못하게 하라.
- 명세관점은 클래스의 안정적인 측면을 드러내야 한다.
- 구현관점은 클래스의 불안정한 측면을 드러내야 한다.
- 인터페이스가 구현 세부사항을 노출하기 시작하면 취약한 설계가 얻어진다.
Review
얼마 전 간단한 숫자 게임을 만들었다. 다 만들고 나서 보니, 기능 구현만 해놓은 볼품없는 코드였다. OOP라는 개념은 수없이 듣고 공부했는데 잘 녹여내지 못했고, 막상 녹여내려 설계하다보니 생각보다 쉽지 않았다.
이 책에서 말하는 객체지향의 목표인 실세계를 모방하는 것이 아닌 새로운 세계를 창조하는 것이다 라는 말을 이해하고, 객체간의 협력을 위한 책임, 역할, 메시지를 이해하니 조금은 설계를 더 재미있게 해볼 수 있을 것 같다는 자신감이 생긴다. 그리고 클래스=도메인 이라고 잘못 생각하고 있던 부분도 짚어주는데, 은연중에 나도 그렇게 생각하고 인터페이스를 잘 활용하지 못했던 부분이 있던 것 같아 잘 머리에 새겨놓았다.
가장 인상깊었던 부분은 디자인 패턴과 TDD에 대해 공부해야할 목적이 생겼다는 것이다. 중요한지 알고는 있지만 피부로 와닿지 않았지 않다보니 흥미도 떨어졌다. 책을 읽은 후, 도메인을 온전히 잘 표현해내기 위해서는 디자인 패턴을 잘 알아놔야겠다고 피부로 느꼈다. 책에서 들어준 몇 개의 도메인 예시에서 내가 만약 여기서 설명하는 패턴을 모르고 있다면 중복 코드가 생기고, 기능이 추가되면 코드가 더러워지고 등등의 worst로 가는 길이 훤히 보였다. 그리고 다시 몇 가지 디자인 패턴을 보니 다르게 보이기 시작했다. (훨씬 재미있게 보여서 좋다) TDD 역시 마찬가지이다. 책임과 역할을 제대로 해내는 기능을 만들어내기 위해, 그리고 불안정한 기능을 위한 핵심 코드만 짜기 위해서는 TDD가 필요하다. 다만 객체지향 원칙을 적용하려는 깊이 있는 고민과 노력이 필요하다는 것을 잊지말자.
처음 읽을 때 뭐야, 당연한거 아니야? 라고 생각했던 내용들이었지만, 실제 실천하지 못하고 있던 부분도 많았고 왜 사람들이 그렇게 중요하다 필요하다고 외쳤는지 체득할 수 있었던 시간이었다. 뿌리를 좀 더 튼튼하게, 그리고 앞으로 나아가야 하는 방향을 추린 느낌이 든다. 아직 구현도 허우적거리는 꼬꼬마 개발자지만 도메인 설계라는 숲을 잘 그리고 싶다는 목표와 의지를 얻은 시간이었다.