오브젝트 책 리뷰 - (1) 역할, 책임, 협력
서론
오브젝트 책을 다 읽은지 벌써 한달 정도가 지나가고 있다. 이 책을 읽으면서 객체지향적으로 코드를 작성하는 게 무엇인지에 대해서 이해하게 되었고 내가 코드를 작성할 때 생각의 방식을 바꾸어준 책이다. 그래서 꼭 이 책은 리뷰를 남겨야 겠다고 생각했다. 총 크게 다섯 파트로 나누어서(내 임의로..) 리뷰를 작성하려 한다. 이번 글에서는 오브젝트 책의 시작에서부터 나오는 역할, 책임, 협력
에 대해서 한번 정리할 것이다.
역할, 책임, 협력
객체지향 설계의 핵심은 협력
을 구성하기 위해 적절한 객체를 찾고 적절한 책임
을 할당하는 과정에서 드러난다
객체지향 설계에서는 하나의 기능을 구현하기 위해 여러 객체들 사이에 메시지를 주고받으면서 협력
을 진행하고 이렇게 하나의 기능을 구현하며 객체가 협력에서 맡은 부분을 책임
이라 한다.
객체의 책임 = 객체가 무엇을 알고 있는가 + 무엇을 할 수 있는가
객체가 협력 안에서 수행하는 책임들이 모여서 객체가 수행하는
역할
을 구성하는데, 예를 들어 위 예시에서 Movie가 DiscountPolicy 라는 인터페이스에 의존한다면 해당 인터페이스는역할
이고, AmountDiscountPolicy, PercentDiscountPolicy 와 같은 각각의 구현 클래스들이책임
이 되는 것이다.협력을 설계하면서 객체의 책임을 식별해 나가는 과정에서 최종적으로 얻게 되는 결과물은 시스템을 구성하는 객체들의 인터페이스와 오퍼레이션의 목록이다.
책임 할당 시 고려해야 할 요소
- 메시지가 객체를 결정한다 (필요한 메시지를 먼저 식별하고 메시지를 처리할 객체를 나중에 선택)
- 행동이 상태를 결정한다
- 객체지향 패러다임에 갓 입문한 사람들이 가장 쉽게 빠지는 실수는 객체의 행동이 아니라 상태에 초점을 맞추는 것임. 초보자들은 먼저 객체에 필요한 상태가 무엇인지를 결정하고, 그 후에 상태에 필요한 행동을 결정한다. 이런 방식은 객체의 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문에 캡슐화를 저해한다. 객체의 내부 구현을 변경하면 퍼블릭 인터페이스도 함께 변경되고, 결국 객체에 의존하는 클라이언트로 변경의 영향이 전파된다. 레베카 워프스브록은 이와 같이 객체의 내부 구현에 초점을 맞춘 설계 방법을
데이터 주도 설계
라고 부르기도 했다. - JPA를 쓰면 데이터 주도 설계가 되기 쉬움. 그래서 그 의존성을 끊으려면 새로운 클래스로 해당 클래스를 변환(dto) 해주어야 함
- 객체지향 패러다임에 갓 입문한 사람들이 가장 쉽게 빠지는 실수는 객체의 행동이 아니라 상태에 초점을 맞추는 것임. 초보자들은 먼저 객체에 필요한 상태가 무엇인지를 결정하고, 그 후에 상태에 필요한 행동을 결정한다. 이런 방식은 객체의 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문에 캡슐화를 저해한다. 객체의 내부 구현을 변경하면 퍼블릭 인터페이스도 함께 변경되고, 결국 객체에 의존하는 클라이언트로 변경의 영향이 전파된다. 레베카 워프스브록은 이와 같이 객체의 내부 구현에 초점을 맞춘 설계 방법을
- 나에게 와닿았던 것은 객체지향 설계는 각 객체별로 책임을 나누어야 한다는 것(이로써 테스트도 쉬워지고, SRP도 충족되고)
- 그러나, 내가 익숙해져야 할 것은 각 객체간에는 메시지만을 호출한다는 것(=인터페이스)이고 그 메시지에는 구현 상세가 포함되어 있지 않아야 한다는 것임 (캡슐화)
책임 주도 설계
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
- 시스템 책임을 더 작은 책임으로 분할
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당 함으로써 두 객체가 협력하게 한다.
책임 주도 설계의 핵심은 책임을 결정한 후(주고 받을 메시지를 정하는 것)에 책임을 수행할 객체를 결정하는 것임
책임주도 설계의 대안
- 책에서는 책임 주도 설계에 대한 상세한 가이드가 나오지만 아직까지 나에게 맞는(더 끌리는..) 방법은 일단 돌아가는 코드를 작성하고 난 후, 코드 상에 명확하게 드러나는 책임을 올바른 위치로 이동시키는 방법임
- 객체로 책임을 분배할 때 가장 먼저 할 일은 메서드를 응집도 있는 수준으로 분해하는 것
- 메서드 응집도 : 메서드가 명령문들의 그룹으로 구성되고 각 그룹에 주석을 달아야 할 필요가 있다면 그 메서드의 응집도는 낮은 것임
- 주석을 추가하는 대신 메서드를 작게 분해해서 각 메서드의 응집도를 높여라
- 중요한 것은 메서드의 이름과 메서드 몸체의 의미적 차이다.
- 메서드를 분리하고 나면 public 메서드는 상위 수준의 명세를 읽는 것 같은 느낌이 듬
1 2 3 4 5 6 7
public Reservation reserve( Screening screening, Customer customer, int audienceCount ) { boolean discountable = checkDiscountable(screening); Money fee = calculateFee(screening, discountable, audienceCount); return createReservation(screening, customer, audienceCount, fee); }
요약
이번 글에서는 역할, 책임, 협력에 대해서 살펴보았다. 역할은 간단하게 말하면 인터페이스의 개념에 상응하고 책임은 해당 인터페이스를 구현하는 각각의 클래스들을 의미했다. 그리고 이러한 역할과 책임을 가진 객체들 끼리의 협력(상호간의 메시지 호출)을 통해서 객체지향 설계가 완성된다. 다음 글에서는 책 전반적으로 계속해서 나오는 응집도, 결합도, 캡슐화에 대해 살펴볼 것이다.