Chapter 03 역할, 책임, 협력
객체지향 패러다임 관점에서 핵심은 역할, 책임, 협력이다.
애플리케이션의 기능을 구현하기 위해 수행하는 상호작용을 협력이라 한다. 협력에 참여하기 위해 수행하는 로직은 책임이다. 객체들이 협력안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다.
협력
자동차 게임에서 자동차는 지정한 숫자보다 크면 앞으로 전진하고 작으면 멈추는 간단한 프로그램을 개발해보자. 그럼 다음과 같이 구현할 수 있다.
public class Car {
private int position;
public Car(int position) {
this.position = position;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
}
public class RacingGame {
private Car car;
public RacingGame(Car car) {
this.car = car;
}
public void race(int number) {
int position = car.getPosition();
if (number > 4) {
car.setPosition(position++);
}
}
}
자율적인 객체
위와 같은 코드에서 Car는 자율성이 훼손되었다. 자율적인 객체란 자신의 상태를 직접 관리하고 스스로의 결정에 따라 행동하는 객체이다. 객체의 자율성을 보장하기 위해서는 필요한 정보와 정보에 기반한 행동을 같은 객체 안에 모아놓아야 한다. Car는 자율적인 존재가 아니라 수동적인 존재가 되었다.
Car가 자율적인 존재가 되려면 자신의 정보를 스스로 변경할 줄 알아야 한다. Car에게 움직이도록 위임을 하게 되면 협력에 참여하는 객체들의 전체적인 자율성을 향상할 수 있다. 객체를 자율적으로 만드는 가장 기본적인 방법이 캡슐화다. 캡슐화가 되어있으면 수정에 용이하므로 자율적인 객체는 변경하기도 쉬워진다. 현재 RacingGame이 Car의 내부 구현에 직접 접근하는 것은 캡슐화를 위반한다. Car에 변경이 일어나면 RacingGame에도 영향을 줄 가능성이 높다. Car가 스스로 움직이도록 구현하면 결합도를 느슨하게 유지하여 변경이 일어나도 RacingGame에 큰 영향을 미치지 않는다. 그럼 Car가 자율적인 객체가 되도록 변경해보자.
public class Car {
private int position;
public Car(int position) {
this.position = position;
}
public void move(int number) {
if (number > 4) {
position++;
}
}
}
public class RacingGame {
private Car car;
public RacingGame(Car car) {
this.car = car;
}
public void race(int number) {
car.move(number);
}
}
이제 Car는 자신의 상태를 스스로 변경할 수 있고 자율적인 객체가 되었다. 객체지향 시스템은 자율적인 객체들의 공동체다. 협력은 객체지향에서 기능을 구현할 수 있는 유일한 방법이다. 협력을 위해 사용할 수 있는 유일한 커뮤니케이션 수단은 메시지 전송이다. 객체는 다른 객체의 내부 구현에 직접 접근할 수 없기 때문에 오직 메시지를 통해서만 자신의 요청을 전달할 수 있다.
이제 RacingGame이 메시지를 전송해서 Car와 협력하게 되었다. 메시지를 수신한 객체는 메서드를 실행하여 요청에 응답한다. 외부의 객체는 메시지만 전송할 수 있으며 어떻게 처리할지는 메시지를 수신한 객체가 직접 결정한다. move라는 메시지를 보냈고 Car는 메시지를 수신하여 움직임을 스스로 결정한다.
협력이 설계를 위한 문맥을 결정한다.
객체는 상태와 행동을 함께 캡슐화하는 실행 단위이다. 객체가 가지는 상태와 행동은 어떤 기준으로 결정하는지 알아보자. 애플리케이션에 어떤 객체가 필요하다면 그 이유는 단 하나여야 한다. 그 객체가 어떤 협력에 참여하고 있고, 객체가 협력에 참여할 수 있는 이유는 협력에 필요한 적절한 행동을 가지고 있기 때문이다.
협력은 객체의 행동을 결정한다. 협력이 바뀌면 행동도 바뀌어야 한다. 협력은 객체가 필요한 이유와 객체가 수행하는 행동의 동기를 제공한다.
행동은 객체의 상태를 결정한다. 객체의 상태는 그 객체가 행동을 수행하는데 필요한 정보가 무엇인지로 결정된다. 객체는 자신의 상태를 스스로 결정하고 관리하는 자율적인 존재이다. 따라서 행동에 필요한 상태도 함께 가지고 있어햐 한다.
결과적으로 협력은 객체를 구성하는 행동과 상태를 모두 결정한다. 따라서 협력은 설계하는 데 필요한 일종의 문맥을 제공한다.
책임
책임은 객체에 의해 정의되는 응집도 있는 행위의 집합이다. 책임은 무엇을 알고 있는가와 무엇을 할 수 있는가로 구성된다.
하는 것
- 객체를 생성하거나 계산을 수행하는 등 스스로 하는 것
- 다른 객체의 행동을 시작하는 것
- 다른 객체의 활동을 제어하고 조절하는 것
아는 것
- 사적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
자동차 게임에서 RacingGame의 책임은 레이싱을 하는 것이다. Car의 책임은 이동하는 것이다. 이건 하는 것의 책임이다. RacingGame은 움직일 자동차를 알아야 한다. 이것은 아는 것과 관련된 책임이다.
처음에 단순한 책임이라고 생각했던 것이 여러 개의 메시지로 분할되기도 한다. 하나의 객체가 수행할 수 있다고 생각한 책임도 나중엔 여러 객체들이 협력해야 하는 큰 책임으로 자라는 것이 일반적이다. 책임의 관점에선 아는 것과 하는 것이 밀접하게 연관되어있다.
객체의 책임
- 자신이 맡은 책임을 수행하는 데 필요한 정보를 알아야 할 책임
- 자신이 할 수 없는 작업을 도와줄 객체를 알고 있어야 할 책임
- 어떤 책임을 수행하기 위해서 그 책임을 수행하는데 필요한 정보도 함께 알아야 할 책임
책임은 객체지향 설계의 핵심이다. 적절한 협력이 적절한 책임을 제공하고, 적절한 책임이 적절한 객체에게 할당해야만 단순하고 유연한 설계를 만들 수 있다.
책임을 할당하는 방법
객체가 책임을 수행하게 하는 유일한 방법은 메시지를 전송하는 것이므로 책임을 할당한다는 것은 메시지의 이름을 결정하는 것과 같다.
메시지를 선택하면 처리할 적절한 객체를 선택한다.
경주를 하기 위해선 자동차를 움직여야 한다. RacingGame은 자동차가 움직이는 것에 대해서는 전문가가 아니다. 새로운 메시지가 필요하다. 자동차를 움직이라는 메시지를 만들고 메시지를 처리할 적절한 객체를 선택한다.
객체지향 설계는 협력에 필요한 메시지를 찾고 메시지에 적절한 객체를 선택하는 반복적인 과정을 통해 이뤄진다. 메시지는 수신하는 객체의 책임을 결정한다.
모든 책임 할당 과정이 이렇게 단순하지는 않다. 기본적인 전략은 책임을 수행할 전문가를 찾는 것이다. 전문가에게 책임을 할당하는 것만으로도 상태와 행동을 가지는 자율적인 객체를 만들 가능성이 높아진다.
책임 주도 설계
책임 주도 설계는 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법이다.
협력이 객체를 설계하기 위한 구체적인 문맥을 제공한다. 협력이 책임을 만들고 책임이 협력에 참여할 객체를 만든다. 구현이 아닌 책임에 집중하는 것이 객체지향 시스템을 위해 가장 중요한 재료기 때문이다.
책임을 할당할 때 고려하는 두 가지 요소
메시지가 객체를 결정한다.
- 객체를 먼저 만드는 것이 아니라 책임을 할당하는 데 필요한 메시지를 먼저 만들고 나서 메시지를 처리할 객체를 선택한다.
- 메시지를 먼저 식별하면 무엇을 수행할지 초점을 맞추는 인터페이스를 얻을 수 있다.
행동이 상태를 결정한다.
- 객체는 협력에 참여하기 위해 존재한다.
- 객체는 협력에 필요한 행동을 제공해야 한다.
- 객체가 협력에 적절한 지 결정하는 것은 상태가 아니라 행동이다.
- 상태는 단지 객체가 행동을 정상적으로 수행하기 위해 필요한 재료다.
중요한 건 객체의 상태가 아니라 행동이다. 상태는 행동을 결정하고 나서야 결정해야 한다. 협력이 객체의 행동을 결정하고 행동이 상태를 결정한다. 그 행동이 객체의 책임이 된다.
역할
역할은 객체가 어떤 특정한 협력에서 수행하는 책임의 집합을 역할이라고 한다. 메시지를 처리하기 위해 RacingGame 객체를 선택했지만 이 과정에는 한 가지가 더 있다. 메시지를 처리하기 위한 적절한 역할이 무엇인가 고민하고 그다음 역할을 수행할 객체로 RacingGame을 선택하는 것이다.
역할 없이 객체만으로 충분할 것 같지만 역할이 중요한 이유는 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있다. 만약 고정 할인 정책과 비율 할인 정책이 있다고 가정할 때 할인 금액을 계산하라는 메시지를 보낸다고 할 때 역할이 없다면 객체가 두 개가 만들어지고 코드 중복이 발생한다.
역할을 구현하는 일반적인 방법은 추상 클래스와 인터페이스를 사용하는 것이다. 책임과 역할을 중심으로 협력을 바라보는 것이 바로 변경과 확장이 용이한 유연한 설계로 나아가는 첫걸음이다. 만약 협력에 책임을 수행하는 대상이 한 종류라면 간단하게 객체로 간주한다. 여러 종류의 객체가 참여한다면 역할이라 부른다.
객체는 다양한 역할을 가질 수 있다. 하지만 협력에 참여할 때 협력 안에서 하나의 역할로 보인다. 다른 협력에 참여한다면 다른 역할로 보인다. 협력 관점에서 동일한 역할을 수행하는 객체는 서로 대체가 가능하다. 또 특정한 객체의 종류를 캡슐화하기 때문에 동일한 역할을 수행하고 다형적이다.
결론
객체지향 설계에서 가장 중요한 것은 책임이다. 객체에게 적절한 책임을 결정하는 것은 협력이다. 협력은 한 객체가 다른 객체에게 도움을 요청할 때 시작된다. 메시지 전송은 협력을 위해 사용하는 유일한 커뮤니케이션 수단이다. 따라서 객체가 책임을 수행하게 하는 유일한 방법은 메시지를 전송하는 것이다. 책임을 할당하는 것은 메시지의 이름을 결정하는 것과 같다. 메시지를 통해 적절한 역할을 찾고 역할에 맞는 적절한 객체를 찾는다. 객체가 존재하는 이유는 협력에 참여하기 위해서다. 객체는 협력에 필요한 행동을 제공해야 하고 행동을 결정하고 나서야 상태가 결정된다. 행동은 객체의 책임이 된다.
처음에 반복적인 단어와 문맥이 잘 읽히지 않아 어려웠다. 천천히 읽으면서 역할, 책임, 협력에 대해 다시 정리하고 공부할 수 있는 시간이었다. 토끼책을 다시 읽어보는 것도 도움이 될 것 같다. 다음 미션에는 책임을 주도적으로 설계를 해보는 방식을 적용해봐야겠다.
조영호 님의 오브젝트를 읽으며 제 기준으로 읽기 편하게 문맥 순서를 바꾼 부분도 있고 생략한 부분도 많습니다. 더 좋은 예제 코드와 깊은 내용을 보기위해 오브젝트를 직접 읽어보는 것을 추천드립니다.
'글쓰기 > 독서' 카테고리의 다른 글
[오브젝트 1, 2장] 객체, 설계, 객체지향 프로그래밍 (0) | 2022.02.15 |
---|---|
[독서 후기] 함께자라기 -애자일로 가는 길 (1) | 2022.01.29 |