본문 바로가기

카테고리 없음

[오브젝트] 객체지향 패러다임

객체지향 패러다임의 관점에서 핵심은 역할, 책임, 협력이다.

객체지향의 본질은 협력하는 객체들의 공동체를 창조하는 것이다. 협력을 구성하기 위해 적절한 객체를 찾고 적절한 책임을 할당하는 과정에서 드러난다.

클래스와 상속은 책임과 협력이 어느정도 설계가 된 후 사용하는 구현 메커니즘일 뿐이다. 설계 이전에 구현에 초점을 맞추는 것은 변경하기 어렵고 유연하지 못한 코드를 낳는 원인이 된다.

협력이란 어떤 기능을 구현하기 위해 다양한 객체들이 메시지를 주고 받으면서 상호작용하는 것을 말한다. 객체가 협력에 참여하기 위해 수행하는 로직은 책임이라고 한다. 객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 역할을 구성한다.

협력

두 객체 사이의 협력은 하나의 객체가 다른 객체에게 도움을 요청할 때 시작된다. 이를 메시지 전송이라고 하며 객체 사이의 협력을 위해 사용하는 유일한 커뮤니케이션 수단이다.

객체는 다른 객체의 상세한 내부 구현에 직접 접근할 수 없기 때문에 메시지 전송을 통해서만 자신의 요청을 전달한다.

메시지를 수신한 객체는 메서드를 실행해 요청에 응답하며 여기서 객체가 메시지를 처리할 방법을 스스로 선택한다.

메시지를 처리하는 책임을 실행하는 도중이 필요한 정보가 객체 스스로에게 없다는 다른 객체에게 협력을 요청할 수 있다. 이렇게 객체들 사이의 협력을 구성하는 일련의 요청과 응답의 흐름을 통해 애플리케이션의 기능이 구현된다.

책임

객체를 설계하기 위해 필요한 문맥이 되는 협력이 갖춰졌을 때 다음으로 할 일은 협력에 필요한 행동을 수행할 수 있는 적절한 객체를 찾고 그 객체가 수행하는 행동을 정한다. 이 행동을 책임이라고 한다.

객체의 책임은 하는 것아는 것 두 가지로 나뉜다.

하는 것

  • 객체를 생성하거나 계산을 수행하는 것
  • 다른 객체의 행동을 시작시키는 것
  • 다른 객체의 활동을 제어하고 조절하는 것

아는 것

  • 사적인 정보에 관해 아는 것
  • 관련된 객체에 관해 아는 것
  • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

영화 상영을 예로 들면

class Screening {
    상영 정보를 알고 있다. => Movie
    예메 정보를 생성한다.
}

class Movie {
    영화 정보를 알고 있다. => DiscountPolicy
    가격을 계산한다.
}

class DiscountPolicy {
    할인 정책을 알고 있다. => DiscountCondition
    할인된 가격을 계산한다.
}

class DiscountCondition {
    할인 조건을 알고 있다. => Screening
    할인 여부를 판단한다.
}

어떤 책임을 수행하기 위해서는 그에 필요한 정보도 함께 알아야할 책임이 있다. 적절한 협력이 적절한 책임을 제공하고 적절한 책임을 적절한 객체에 할당해야만 단순하고 유연한 설계를 창조할 수 있다.

객체지향에서 가장 중요한 것은 책임이다. 객체에게 얼마나 적절한 책임을 할당하느냐가 설계의 전체적인 품질을 결정한다. 객체의 구현 방법은 상대적으로 책임보다는 덜 중요하며 책임을 결정한 다음에 고민해도 늦지 않다.

책임 할당

협력에 필요한 지식을 다른 객체에 요청하고 이 요청에 응답하기 위해 객체가 수행할 책임이 정해진다. 즉 책임을 할당하려면 협력이라는 문맥을 정의해야 한다.

결국 협력을 위해 다른 객체에 보내는 메시지가 다른 객체를 결정하게 되고 그 객체가 메시지를 처리하기 위해 어떠한 책임을 가지게 되고, 그 책임에 필요한 정보를 그 객체가 상태로서 갖게 된다.

여기서 객체지향 패러다임에 갓 입문한 사람들이 가장 쉽게 빠지는 실수가 있는데, 객체의 행동이 아니라 상태에 초점을 맞춘다는 것이다. 초보자들은 객체의 상태를 먼저 결정하고 그 후에 상태에 따른 행동을 정한다. 이렇게 하면 객체 내부 구현이 객체의 퍼블릭 인터페이스에 노출되도록 만들기 때문에 캡슐화를 저해한다.

중요한 것은 객체의 상태가 아니라 행동이다. 객체가 가질 수 있는 상태는 행동을 결정하고 나서 결정할 수 있다. 협력이 객체의 행동을 결정하고 행동이 상태를 결정한다. 그리고 그 행동이 바로 객체의 책임이 된다.

역할

객체는 협력이라는 주어진 문맥 안에서 특정한 목적을 갖게 된다. 객체의 목적은 협력 안에서 객체가 맡게 되는 책임의 집합으로 표시되고 이 책임의 집합을 역할이라고 한다.

실제로 협력을 모델링할 때는 특정한 객체가 아니라 역할에게 책임을 할당한다고 생각하는 게 좋다.

여기서 책임 할당을 하게되는 과정 자체가 사실은 책임을 할당 하며 그 객체에 역할을 준다고 할 수 있다.

불필요한 과정처럼 보이지만 역할을 통해 유연하고 재사용 가능한 협력을 얻을 수 있다. 예를 들어 넷플릭스의 경우 요금 플랜에 따라 다른 화질의 영상을 보여주는데, 이 요금 대비 화질 계산을 비디오 객체가 하도록 역할을 주면 비디오 객체에 중복된 코드가 많이 생길 수 있다.

이러한 요금 대비 화질 계산은 요금 정책이라는 객체를 따로 두고 이 객체가 전부 계산하도록 적절히 역할을 분배할 필요가 있다.

비디오 객체가 요금 정책이라는 객체에 화질 계산을 요청하고 이 요금 정책은 다른 요금 정책 객체와 슬롯처럼 갈아끼울 수 있다. 이 슬롯이 바로 역할에 해당한다. 역할은 다른 것으로 교체할 수 있는 책임의 집합이다.

설계 할 때 대부분의 경우에 어떤 것이 역할이고 어떤 것이 객체인지 또렷하게 드러나지 않는다. 특히나 정보가 부족한 설계 초반에는 기준을 세우고 결정을 내리기가 어렵다.

저자의 개인적의 견해는 설계 초반에 적절한 책임과 협력의 큰 그림을 탐색하는 것이 가장 중요한 목표이고, 역할과 객체를 명확하게 구분하는 것은 그렇게 중요하지는 않다고 했다.

단순한 객체로 시작하고 반복적으로 책임과 협력을 정제해가면서 필요한 순간에 객체로부터 역할을 분리해나가는 방법이 좋다고 한다.