Help us understand the problem. What is going on with this article?

객체지향의 사실과 오해

More than 1 year has passed since last update.

객체지향의사실과오해_Cover.jpg

한줄평

개발자가 본 이상한 나라의 앨리스. 그리고 역할, 책임, 협력

책소개

이 책은 2015년에 출판된 책이다. 저자는 조용호님이다. 객체지향에 대해 프로그래밍 언어를 중심으로 설명한게 아니라 원론적인 개념을 중심으로 설명한 책이다. 페북친구안 안영회님이 인터뷰를 올린 블로그가 있어 책을 보고 나서 읽어보면 저자에 대해 조금 더 알 수 있다.
인터뷰: 객체지향의 사실과 오해 저자 조영호씨에게 물음

감상평

먼저 이 책을 읽게 된 것은 객체지향에 대해 좀 더 알고 싶다라는 마음에 어떤 책이 좋을 지 찾다가 참여 중인 개발자 독서 모임(개발자 한 달에 책 한 권 읽기)에서 토론하고 추천한 책이라 보게 되었다. 그동안은 객체지향을 알려하면 매번 클래스를 가지고 캡슐화, 상속, 다형성을 설명하는 게 대부분이였다. 특정 언어의 문법 위주와 자동차에 대한 객체 비유만으로는 구현할 때 내가 만들어야 하는 상황에서 객체를 어떻게 찾고 구성해야 하는지를 알 수 가 없었다. 대학수업과 박영만 학원 SE과정에서 배웠던 바에서도 프로젝트에 대한 문제 정의, 요구사항을 정리한 다음 거기서 동적인 부분과 정적인 부분(대개 명사 단어와 행위 임)을 추출해서 클래스를 하나씩 만들고 관계를 연결하는 것이 설명이였다. 하지만 문제 정의와 요구사항에서 막연히 뽑아내는게 쉽지 않았다.

이 책에서 보여준 역할, 책임, 협력 관점에서 객체를 찾는 과정은 이전에 하기 힘들어 했던 부분을 조금은 쉽게 풀어낼 수 있을 것 같다는 생각이 들게 하였다. 책에서 핵심으로 얘기한 아래 문구가 내가 그동안 고정적으로 생각하던 것을 깨주는 것이였다.

널리 알려진 미신과 달리 객체지향의 핵심은 클래스가 아니다. 핵심은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계를 구축하는 것이다. 클래스는 협력에 참여하는 객체를 만드는 데 필요한 구현 매커니즘일 뿐이다. 클래스가 중요하지 않다는 것이 아니라 단지 협력 구죠와 책임을 식별하는 것에 비해 상대적으로 덜 중요하다는 것을 말하고 싶은 것이다.\
... \
클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하라. 객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다.

이 책의 특징은 이상하 나라의 앨리스 동화를 소재로 하여 역할, 책임, 협력을 설명하는 방법이 재미있게 다가왔으며, 이 것들을 이해하는 데 나에게 도움이 되었으니 저자의 소재 선택이 좋았다고 판단한다. 또 하나의 특징은 초심자를 위한 것이 아니라 설계, 구현의 경험이 있는 독자를 대상이라는 점이다. 책을 읽다 보면 "당신이 그동안은 이렇게 생각했을 테지만 사실은 이렇다"는 느낌을 자주 받았다. 사실 저자가 서문의 대상 독자에서도 객체지향 프로그래밍 언어를 사용하는 소프트웨어 개발자를 대상으로 했다고 쓰여 있다. 내 생각엔 구현보다는 특히 설계를 했던 경험이 있는 독자가 봐야 도움이 될 것 같다는 느낌이 든다.

좋은 객체지향 구조로 프로그래밍을 하고 싶다라는 바램이 있는 사람들이 읽어보기에 좋은 책이다.

마지막으로 객체지향에 대한 책을 보면서 객체라는 단어 표현에 불편함이 있다. 우리나라에서 Object를 객체로 표현하는 것에는 반대하는 편이다. 이에 대해서는 개발자 Pope Kim님의 의견에 동의한다. 사전에 객체를 찾아보면 주체로부터 독립되어 있는 인간의 인식과 실천의 대상, 문장 내에서 동사의 행위가 미치는 대상이라고 나온다. 그리고 객체주체의 반대말이다. 사전적의미로 보면 어떤 행위를 당하는 대상에 초점이 맞춰져 있는 느낌이다.
(출처: 다음사전 우리말샘)

이 보다는 개체라는 단어가 더 어울리는 것 같다. 사전적의미로 보면 단일하고 고유한 독자적 존재, 하나의 생물로서 생존하는 데 필요한 기능과 구조를 갖춘 최소의 단위와 같다. 사물, 동물, 존재처럼 단일한 물체나 개념에 개체가 더 어울리는 느낌이 있다.
(출처: 다음사전 우리말샘)

내용 갈무리

01 협력하는 객체들의 공동체

시너지를 생각하라. 전체는 부분의 합보다 크다\
- 스티븐 코비(Stephen R. Covey)

아쉽게도 실세계의 모방이라는 개념은 객체지향의 기반을 이루는 철학적인 개념을 설명하는 데는 적합하지만 유연하고 실용적인 관점에서 객체지향 분석, 설계를 설명하기에는 적합하지 않다.

객체지향이 실세계를 모방하는게 아니라는 그동안의 많은 통념을 깨는 주장이 흥미롭다.

사람들은 다른 사람과 협력하는 과정 속에서 특정한 역할(role)을 부여받는다.\
... \
역할은 어떤 협력에 참여하는 특정한 사람이 협력 안에서 차지하는 책임이나 임무를 의미한다.\
... \
방금 책임(responsibility)이라는 단어를 사용했던가? 그렇다. 역할이라는 단어는 의미적으로 책임이라는 개념을 내포한다.\
... \
특정한 역할은 특정한 책임을 암시한다.

협력, 역할, 책임이 가지는 중요한 개념

  • 여러 사람이 동일한 역할을 수행할 수 있다
  • 역할은 대체 가능성을 의미한다
  • 책임을 수행하는 방법은 자율적으로 선택할 수 있다
  • 한 사람이 동시에 여러 역할을 수행할 수 있다

과거의 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분한다. 이에 반해 객체지향에서는 데이터와 프로세스를 객체라는 하나의 틀 안에 함께 묶어 놓음으로써 객체의 자율성을 보장한다.

이 말에 동의

02 이상한 나라의 객체

객체지향 패러다임은 지식을 추상화하고 추상화한 지식을 객체 안에 캡슐화함으로써 실세계 문제에 내재된 복잡성을 관리하려고 한다. 객체를 발견하고 창조하는 것은 지식과 행동을 구조화하는 문제다.\
- 레베카 워프스브룩(Rebecca Wirfs-Brock)

객체지향 패러다임의 목적은 현실 세계를 모방하는 것이 아니라 현실 세계를 기반으로 새로운 세계를 창조하는 것이다.

내가 알고 있던 바를 깨는 주장이다. 근데 동의하게 된다.

객체의 다향한 특성을 효과적으로 설명하기 위해서는 객체를 상태(state), 행동(behavior), 식별자(identity)를 지닌 실체로 보는 것이 가장 효과적이다.

일반적으로 과거에 발생한 행동의 이력을 통해 현재 발생한 행동의 결과를 판단하는 방식은 복잡하고 번거로우며 이해하기 어렵다. 따라서 인간은 행동의 과정과 결과를 단순하게 기술하기 위해 상태라는 개념을 고안했다.

상태 개념의 배경

단순한 값들은 그 자체로 독립적인 의미를 가지기보다는 다른 객체의 특성을 표현하는데 사용된다.

객체의 프로퍼티는 단순한 값인 속성과 다른 객체를 가리키는 링크라는 두 가지 종류의 조합으로 표현할 수 있다.

객체의 행동에 의해 객체의 상태가 변경된다는 것은 행동이 부수 효과(side effect)를 초래한다는 것을 의미한다.\
... \
앨리스가 케이크를 먹는 행위는 앨리스의 키를 작게 변화시키고 케이크의 양을 줄이는 부수 효과를 야기한다.

부수 효과를 설명한 예제가 좋음.

상태라는 개념을 이용해 행동을 서술한 두 가지 관점

  • 상호작용이 현재의 상태에 어떤 방식으로 의존하는가?
  • 상호작용이 어떻게 현재의 상태를 변경시키는가?

앨리스는 직접적으로 음료의 상태를 변경할 수 없다. 단지 음료에게 자신이 음료를 마셨다는 메시지를 전달할 수 있는 뿐이다.

확실히 현실세게와의 차이점을 설명하는 문구다.

메시지를 앨리스에게 전송하는 객체이건 음료에게 메시지를 전송하는 앨리스 객체이건 메시지 송신자는 메시지 수신자의 상태 변경에 대해서는 전혀 알지 못한다.\
이것이 캡슐화가 의미하는 것이다.

값과 객체의 가장 큰 차이점은 값은 식별자를 가지지 않지만 객체는 식별자를 가진다는 점이다.

값(value)은 숫자, 문자열, 날짜, 시간, 금액 등과 같이 변하지 않는 양을 모델링한다. 흔히 값의 상태는 변하지 않기 떄문에 불변 상태(immutable state)를 가진다고 한다.

상태를 이용해 두 값이 같은지 판단할 수 있는 성질을 동등성(equality)이라고 한다. 상태를 이용해 동등성을 판단할 수 있는 이유는 값의 상태가 변하지 않기 때문이다.

동등성으로 값을 판단하는 이유

객체는 시간에 따라 변경되는 상태를 포함하며, 행동을 통해 상태를 변경한다. 따라서 객체는 가변 상태(mutable state)를 가진다고 말한다.

객체 역시 사람과 유사하게 상태와 무관하게 두 객체를 동일하거나 다르다고 판단할 수 있는 프로퍼티를 가진다. 두 객체의 상태가 다르더라도 식별자가 같다면 두 객체를 같은 객체로 판단할 수 있다. 이처럼 식별자를 기반으로 객체가 같은지 판단할 수 있는 성질을 동일성(identical)이라고 한다.\
상태를 기반으로 객체의 동일성을 판단할 수 없는 이유는 시간이 흐름에 따라 객체의 상태가 변하기 때문이다.

참조 객체(reference object), 또는 엔티티(entity)는 식별자를 지닌 전통적인 의미의 객체를 가리키는 용어다. 값 객체(value object)는 식별자를 가지지 않는 값을 가리키는 용어다.

왜 상태를 먼저 결정하고 행동을 나중에 결정하는 방법은 설계에 나쁜 영향을 미치는가?

  1. 상태를 먼저 결정할 경우 캡슐화가 저해된다.
  2. 객체를 협력자가 아닌 고립된 섬으로 만든다. 객체가 필요한 이유는 애플리케이션의 문맥 내에서 다른 객체와 협력하기 위해서다.
  3. 객체의 재사용성이 저하된다.

협력에 참여하는 훌륭한 객체 시민을 양성하기 위한 가장 중요한 덕목은 상태가 아니라 행동에 초점을 맞추는 것이다.\
... \
설계자로서 우리는 협력의 문맥에 맞는 적절한 행동을 수행하는 객체를 발견하거나 창조해야 한다.

이 내용으로 보면 상태를 먼저 고려하는 방식은 구조적 프로그래밍 방식의 특징이다.

협력 안에서 객체의 해동은 결국 객체가 협력에 참여하면서 완수해야 하는 책임을 의미한다. 따라서 어떤 책임이 필요한가를 결정하는 과정이 전체 설계를 주도해야 한다.\
책임-주도 설계(Responsibility-Driven Design, RDD)

"행동이 상태를 결정한다"

모방과 추상화라는 개념만으로 현실 객체와 소프트웨어 객체 사이의 관계를 깔끔하게 설명하지 못한다.

그렇다면 현실 속의 객체와 소프트웨어 객체 사이의 가장 큰 차이점은 무엇일까? 그것은 현실 속에서는 수동적인 존재가 소프트웨어 객체로 구현될 떄는 능동적으로 변한다는 것이다.

소프트웨어 안에 구축되는 객체지향 세계는 현실을 모방한 것이 아니다. 현실의 모습을 조금 참조할 뿐 궁극적인 목적은 현실과 전혀 다른 새로운 세계를 창조하는 것이다.

은유는 표현적 차이(representataional gap) 또는 의미적 차이(semantic gap)라는 논점과 관련성이 깊다. 여기서 차이란 소프트웨어에 대해 사람들이 생각하는 모습과 실제 소프트웨어의 표현 사이의 차이를 의미한다.

트럼프 카드도 쉽게 뒤집힐 것이고 채셔 고양이가 야웅 소리를 내며 울고 토끼가 빠르게 달릴 수 있으리라는 것을 쉽게 예상할 수 있다. 이것은 루이스 캐럴이 현실 속의 객체를 바탕으로 은유를 통해 이상한 나라의 객체를 묘사하고 있기 때문이다. 객체지향 설계자로서 우리의 목적은 현실을 모방하는 것이 아니다. 단지 이상한 나라를 창조하기만 하면 된다.

03 타입과 추상화

일단 컴퓨터를 조작하는 것이 추상화를 구축하고, 조작하고, 추론하는 것에 관한 모든 거이라는 것을 깨닫고 나면 (훌륭한) 컴퓨터 프로그램을 작성하기 위한 중요한 전제 조건은 추상화를 정확하게 다루는 능력이라는 것이 명확해진다.\
- 키스 데블린(Keith Devlin)

1930년대 5개의 지하철 회사를 통폐합하고 언더그라운드(Underground)라는 새로운 브랜드로 공동 운영하게 된 런던 지하철은 전 노선을 쉽게 알아볼 수 있는 지도를 만들기로 결정한다. 초기의 지하철 노선도는 실제와 유사한 물리적인 지형 위에 구불구불한 운행 노선과 불규칙적인 역간의 거리를 사실적으로 묘사하고 있었다. 문제는 이렇게 사실적인 정보가 오히려 지하철을 이용하는 승객들로 하여금 노선도를 이해하기 어렵게 만들었다는 점이다.\
\
지하철을 이용하는 승객의 목적은 하나의 역에서 다른 역으로 이동하는 것이다.

헤리 벡이 창조한 지하철 노선도의 핵심은 지도가 당연히 가져야 한다고 생각되는 '정확성'을 버리고 그 '목적'에 집중한 결과다.

진정한 의미에서 추상화란 현실에서 출발하되 불필요한 부분을 도려가면서 사물의 놀라운 본질을 드러나게 하는 과정이라고 할 수 있다.

공통점을 기반으로 객체들을 묶기 위한 그릇을 개념(Concept)이라고 한다.

개념의 세 가지 관점

  • 심볼(symbol): 개념을 가리키는 간력한 이름이나 명칭
  • 내연(intension): 개념의 완전한 정의를 나타내며 내연의 의미를 이용해 객체가 개념에 속하는지 여부를 확인할 수 있다.
  • 외연(extension): 개념에 속하는 모든 객체의 집합(set)

분류란 객체에 특정한 개념을 적용하는 작업이다. 객체에 특정한 개념을 적용하기로 결심했을 때 우리는 그 객체를 특정한 집합의 멤버로 분류하고 있는 것이다.

타입의 정의는 개념의 정의와 완전히 동일하다.

안타깝게도 타입이 근본적으로 개념과 동일하다고 하더라도 일단 컴퓨터 내부로 들어오는 순간 좀 더 기계적인 의미로 윤색될 수 밖에 없다.

타입 시스템의 목적은 메모리 안의 모든 데이터가 비트열로 보임으로써 야기되는 혼란을 방지하는 것이다.

타입에 관련한 두 가지 중요한 사실

  1. 타입은 데이터가 어떻게 사용되느냐에 관한 것이다.
  2. 타입에 속한 데이터를 메모리에 어떻게 표현하는지는 외부로부터 철저하게 감춰진다.

객체의 타입에 관련한 두 가지 중요한 사실

  1. 어떤 객체가 어떤 타입에 속하는지 결정하는 것은 객체가 수행하는 행동이다.
  2. 객체의 내부적은 표현은 외부로부터 철저하게 감춰진다.

같은 타입에 속한 객체는 행동만 동일하다면 서로 다른 데이터를 가질 수 있다. 여기서 동일한 행동이란 동일한 책임을 의미하며, 동일한 책임이란 동일한 메시지 수신을 의미한다. 따라서 동일한 타입에 속한 객체는 내부의 데이터 표현 방식이 다르더라도 동일한 메시지를 수신하고 이를 처리할 수 있다. 다만 내부의 표현 방식이 다르기 떄문에 동일한 메시지를 처리하는 방식은 서로 다를 수밖에 없다. 이것은 다형성에 의미를 부여한다. 다형성이란 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력을 뜻한다. 동일한 메시지를 서로 다른 방식으로 처리하기 위해서는 객체들은 동일한 메시지를 수신할 수 있어야 하기 떄문에 결과적으로 다형적인 객체들은 동일한 타입(또는 타입 계층)에 속하게 된다.

객체를 결정하는 것은 행동이다. 데이터는 단지 행동을 따를 뿐이다. 이것이 객체를 객체답게 만드는 가장 핵심적인 원칙이다.

트럼프는 트럼프 인간을 포관하는 좀 더 일반적인 개념이다. 트럼프 인간은 트럼프보다 좀 더 특화된 행동을 하는 특수한 개념이다. 이 두 개념 사이의 관계를 일반화/특수화(generalizatino/specialization) 관계라고 한다.

객체의 일반화/특수화 관계에 있어서도 중요한 것은 객체가 내부에 보관한 데이터가 아니라 객체가 외부에 제공하는 행동이다.

여기서 주의해야 할 점은 타입은 내연을 의미하는 행동의 가짓수와 외연을 의미하는 집합의 크기는 서로 반대라는 사실이다.

일반적으로 서브타입은 슈퍼타입의 행위와 호환되기 떄문에 서브타입은 슈퍼타입을 대체할 수 있어야 한다.

어떤 타입을 다른 타입의 서브타입이라고 말할 수 있으려면 다른 타입을 대체할 수 있어야 한다.

객체지향 패러다임을 통해 세상을 바라보는 거의 대부분의 경우에 분류와 일반화/특수화 기법을 동시에 적용하게 된다.

어떤 시점에 앨리스에 관해 생각할 때 불필요한 시간이라는 요소와 상태 변화라는 요소를 제거하고 철저하게 정적인 관점에서 앨리스의 모습을 묘사하는 것을 가능하게 해준다.

클래스와 타입은 동일한 것이 아니다.

클래스는 타입의 구현 외에도 코드를 재사용하는 용도로도 사용되기 때문에 클래스와 타입을 동일시하는 것은 수많은 오해와 혼란을 불러일으키곤 한다.

클래스는 타입을 구현하기 위해 프로그래밍 언어에서 제동하는 구현 메커니즘이라는 사실을 기억하라.

04 역할, 책임, 협력

우리 모두를 합친 것보다 더 현명한 사람은 없다\
- 켄 블랜타드(Ken Blanchard)

객체지향에 갓 입문한 사람들의 가장 흔한 실수는 협력이라는 문맥을 고려하지 않은 채 객체가 가져야 할 상태와 행동부터 고민하기 시작한다는 것이다.

훌륭한 객체지향 설계란 겉모습은 아름답지만 협력자들을 무시하는 오만한 객체를 창조하는 것이 아니라 조화를 이루며 적극적으로 상화작용하는 협력적인 객체를 창조하는 것이다. 비록 그 객체를 따로 떼어놓고 봤을 때는 겉모습이 다소 기묘하고 비합리적이더라도 말이다.

어떤 등장인물들이 특정한 요청을 받아들일 수 있는 이유는 그 요청에 대해 적절한 방식으로 응답하는 데 필요한 지식과 행동 방식을 가지고 있기 때문이다. 그리고 요청과 응답은 협력에 참여하는 객체가 수행할 책임을 정의한다.

객체의 책임은 '객체가 무엇을 알고 있는가(knowing)와 '무엇을 할 수 있는가(doing)'로 구성된다.

책임이 협력이라는 문맥 속에서 요청을 수신하는 한 쪽의 객체 관점에서 무엇을 할 수 있는지를 나열하는 것이라면 메시지는 협력에 참여하는 두 객체 사이의 관계를 강조한 것이다.

설계를 시작하는 초반에는 어떤 객체가 어떤 책임을 가지고 어떤 방식으로 서로 협력해야 하는지에 대한 개요를 아는 것만으로도 충분하다. 책임과 협력의 구조가 자리를 잡기 전까지는 책임을 구현하는 방법에 대한 고민은 잠시 뒤로 미루는 것이 좋다.

어떤 객체가 수행하는 책임의 집합은 객체가 협력 안에서 수행하는 역할을 암시한다. 이것이 중요한가? 왕은 왕일 뿐이고 모자 장수는 모자 장수일 뿐이지 않는가? 굳이 왕을 판사라고 부르고 모자 장수를 증인이라고 불러서 상황을 복잡하게 만드는 이유는 무엇인가?\
\
그것은 역할이 재사용 가능하고 유연한 객체지향 설계를 낳는 매우 중요한 구성요소이기 때문이다.

역할은 객체지향 설계의 단순성(simplicity), 유연성(flexibility), 재사용성(reusability)을 뒷받침하는 핵심 개념이다.

일반화/특수화 관점에서 좀 더 일반적인 개념을 의미하는 역할은 일반화이며 좀 더 구체적인 개념을 의미하는 객체의 타입은 특수화다.

요약하면 역할의 대체 가능성은 행위 호환성을 의미하고, 행위 호환성은 동일한 책임의 수행을 의미한다.

역할 대체 가능성 = 행위 호환성 = 동일한 책임의 수행

견고한 객체지향 시스템이 보장하기 위해서는 전체 개발 단계에 걸쳐 객체의 역할과 책임, 협력을 도드라지게 만드는 기법과 체계를 따르는 것이 중요하다.

앨리스터 코오번에 따르면 효과적으로 일하는 사람들의 한 가지 특징은 아무것도 없는 상태에서 작업을 시작하지 않고 이전의 훌륭한 결과물을 모방하고 약간의 수정을 거쳐 원하는 결과물을 만들어 낸다는 것이다.

객체지향에 대한 경험이 적은 초보자들은 개발을 주도하기 위해 어떤 테스트를 어떤 식으로 작성해야 하는지를 결정하는 데 큰 어려움을 느낀다. 테스트-주도 개발은 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각하라고 충고한다.

테스트-주도 개발은 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다.

테스트를 작성하기 위해 객체의 메서드를 호출하고 반환값을 검증하는 것은 순간적으로 객체가 수행해야 하는 책임에 관해 생각한 것이다.

테스트-주도 개발 방법과 책임이 연결되는 대목이다.

05 책임과 메시지

의도는 "메시징"이다. 훌륭하고 성장 가능한 시스템을 만들기 위한 핵심은 모듈 내부의 속성과 행동이 어떤가보다는 모듈이 어떻게 커뮤니케이션하는가에 달려있다.\
-앨런 케이

자율성의 사전적 의미는 '자기 스스로의 원칙에 따라 어떤 일을 하거나 자신을 통제해서 절제하는 성질이나 특성'이다.\
... \
따라서 자율적인 객체란 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 행동하는 객체다. 타인이 정한 규칙이나 명령에 따라 판단하고 행동하는 객체는 자율적인 객체라고 부르기 어렵다.

사실 자율적인 객체가 말이되나? 내가 종이나 컴퓨터로 작성하는 게 객체인데 그 객체가 자율성을 띤다는게 말이 되나? 저자가 한 이말을 다시 생각해보면 부처님 모습을 조각하는 석공을 생각하게 된다.
어느 석공이 말한것 처럼 내가 돌을 깍아서 만드는게 아니라 이미 돌 안에 부처님이 있고 난 단지 돌에서 부처님을 끄집어 내는 것 일뿐이다. 종이와 컴퓨터에 있는 객체를 끄집어 내라는 의미 같다.

자율적인 책임의 특징은 객체가 '어떻게(how)'해야 하는가가 아니라 '무엇(what)'을 해야 하는가를 설명한다는 것이다.

메시지를 수신받은 객체는 우선 자신이 해당 메시지를 처리할 수 있는지 확인한다. 메시지를 처리할 수 있다는 이야기는 객체가 해당 메시지에 해당하는 행동을 수행해야 할 책임이 있다는 것을 의미한다. 따라서 근본적으로 메시지의 개념은 책임의 개념과 연결된다.

객체는 메시지를 처리하기 위한 방법을 자율적으로 선택할 수 있다.

다형성이란 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응하는 것을 의미한다.

따라서 다형성을 하나의 메시지와 하나 이상의 메서드 사이의 관계로 불 수 있다.

객체지향 패러다임이 강력한 이유는 다형성을 이용해 협력을 유연하게 만들 수 있기 때문이라는 점을 기억하라.

유연하고 확장 가능하고 재사용성이 높은 협력의 의미

  1. 협력이 유연해진다. 송신자는 수신자가 메시지를 이해한다면 누구라도 상관하지 않는다.
  2. 협력이 수행되는 방식을 확장할 수 있다. 송신자에게 아무런 영향도 미치지 않고서도 수신자를 교체할 수 있기 때문에 협력의 세부적인 수행 방식을 쉽게 수정할 수 있다.
  3. 협력이 수행되는 방식을 재사용할 수 있다. 협력에 영향을 미치지 않도서도 다양하 객체들이 수신자의 자리를 대체할 수 있기 때문에 다양한 문맥에서 협력을 재사용할 수 있다.

협력 관계 속에서 다른 객체에게 무엇을 제공해야 하고 다른 객체로부터 무엇을 얻어야 하는가라는 관점에서 접근할 때만 훌륭한 책임을 수확할 수 있다.

결과적으로 메시지가 수신자의 책임을 결정한다.

송신자는 수신자가 어떤 객체인지는 모르지만 자신이 전송한 메시지를 잘 처리할 것이라는 것을 믿고 메시지를 전송할 수 밖에 없다. 이런 스타일의 협력 패턴은 '묻지 말고 시켜라'라는 이름으로 널리 알려져 있다.

인터페이스의 세 가지 특징

  1. 인터페이스의 사용법을 익히기만 하면 내부 구조나 동작 방식을 몰라도 쉽게 대상을 조작하거나 의사를 전달할수 있다.
  2. 인터페이스 자체는 변경하지 않고 단순히 내부 구성이나 작동 방식만을 변경하는 것은 인터페이스 사용자에게 어떤 영향도 미치지 않는다.
  3. 대상이 변경되더라도 동일한 인터페이스를 제공하기만 하면 아무런 문제 없이 상호작용 할 수 있다.

책임의 자율설이 협력의 품질을 결정하는 이유는?

  1. 자율적인 책임은 협력을 단순하게 만든다.
  2. 자율적인 책임은 모자 장수의 외부와 내부를 명확하게 분리한다.
  3. 책임이 자율적일 경우 책임을 수행하는 내부적인 방법을 변경하더라도 외부에 영향을 미치지 않는다.
  4. 자율적인 책임은 협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다.
  5. 객체가 수행하는 책임들이 자율적일수록 객체의 역할을 이해하기 쉬워진다.

06 객체 지도

유일하게 변하지 않는 것은 모든 것이 변한다는 사실뿐이다.\
- 헤라클레이토스(Heraclitus of Ephesus)

다른 사람에게 길을 물어보는 첫 번째 방법은 '기능적이고 해결책 지향적인 접근법(functional, solution-directed approach)'이다.
... \
이 방법은 일반적이지도, 재사용 가능하지도 않다. 강이나 산과 같은 중요한 랜드마크가 없다면 경로를 설명하기 어려울 뿐만 아니라 설명만으로 경로를 찾기도 쉽지 않다.

지도를 이용하는 방법은 '구조적이고 문제 지향적인 적근법(structural, problem-directed approach)'이다. 지도는 길을 찾는 데 필요한 구체적인 기능이 아니라 길을 찾을 수 있는 '구조'를 제공한다.\
... \
지도는 현재의 목적뿐만 아니라 다양한 목적을 위해 재사용될 수 있다. 즉, 지도는 범용적이다.

지도가 범용적인 이유는 지도를 사용하려는 사람들이 원하는 '기능'에 비해 지도에 표시된 '구조'가 더 안정적이기 떄문이다.

자주 변경되는 기능이 아니라 안정적인 구조를 따라 역할, 책임, 협력을 구성하라.

지도 은유를 통해 살펴본 것처럼 변경에 대비하고 변경의 여지를 남겨 놓는 가장 좋은 방법은 자주 변경되는 기능이 아닌 안정적인 구조를 중심으로 설계하는 것이다. 전통적인 기능 분해(functional decomposition)는 자주 변경되는 기능을 중심으로 설계한 후 구조가 기능에 따르게 한다. 이럿이 바로 전통적인 기능 분해 방법이 변경에 취약한 이유다.

이에 비해 객체지향 접근방법은 자주 변경되지 않는 안정적인 객체 구조를 바탕으로 시스템 기능을 객체 간의 책임으로 분배한다. 객체지향은 객체의 구조에 집중하고 기능이 객체의 구조를 따르게 만든다. 시스템 기능은 더 작은 책임으로 분할되고 적절한 객체에게 분배되기 때문에 기능이 변경되더라도 객체 간의 구조는 그대로 유지된다.

소프트웨어를 사용하는 사람들은 자신이 관심을 가지고 있는 특정한 분야의 문제를 해결하기 위해 소프트웨어를 사용한다. 이처럼 사용자가 프로그램을 사용하는 대상 분야를 도메인이라고 한다.

은행 업무에 종사하는 사람들은 은행 도메인을 고객 계좌 사이의 돈의 흐름으로 이해할 것이다. 중고 자동차 판매상은 구매되는 자동자화 판매된느 자동차의 교환으로 자동체 도메인을 바라볼 것이다. 게임 플레이어들은 게임 도메인을 캐릭터와 몬스터, 그리고 몬스터가 떨구는 아이템 간의 관계로 파악한다. 프로그래밍에 종사하는 사람들은 프로그래밍 도메인을 커피를 입력으로 코드를 출력하는 함수로 파악한다. 소프트웨어의 도메인이 무엇이건 상관없이 그곳에는 항상 도메인과 관련된 사람들이 그곳에는 항상 도메인과 관련된 사람들이 도메인을 바라보는 모델이 존재한다.

도메인 용어 이해를 돕는 예

도메인 모델은 이해관계자들이 바라보는 멘탈 모델(mental model)이다. 멘탈 모델이란 사람들이 자기 자신, 다른 사람, 환경, 자신이 상호작용하는 사물들에 대해 갖는 모형이다.

도널드 노먼은 멘탈 모델을 사용자 모델, 디자인 모델, 시스템 이미지의 세 가지로 구분한다. 사용자 모델은 사용자가 제품에 대해 가지고 있는 개념들의 모습이다. 디자인 모델은 설계자가 마음 속에 갖고 있는 시스템에 대한 개념화다. 시스템 이미지는 최종 제품이다.

소프트웨어 객체는 현실 객체를 모방한 것이 아니라 은유를 기반으로 재창조한 것이다.\
... \
이처럼 소프트웨어 객체와 현실 객체 사이의 의미적 거리를 가리켜 표현적 차이 또는 의미적 차이라고 한다.

우리가 은유를 통해 투영해야 하는 대상은 무엇인가? 그것은 바로 사용자 도메인에 대해 생각하는 개념들이다.

유스케이스의 몇 가지 특성

  1. 사용자와 시스템 간의 상호작용을 보여주는 '텍스트'다. 유스케이스의 핵심은 사용자와 시스템 간의 상호작용을 일련의 이야기 흐름으로 표현하는 것이다.
  2. 하나의 시나리오가 아니라 여러 시나리오의 집합이다.
  3. 단순한 피처(feature) 목록과 다르다.
  4. 사용자 인터페이스와 관련된 세부 정보를 포함하지 말아야 한다. 자주 변경되는 사용자 인터페이스 요소는 배제하고 사용자 관점에서 시스템의 행위에 초점을 맞춘다.
  5. 내부 설계와 관련된 정보를 포함하지 않는다.

유스케이스는 설계 기법도, 객체지향 기법도 아니다\
... \
유스케이스는 시스템이 외부에 제공해야 하는 행위만 포함하기 떄문에 유스케이스로부터 시스템의 내부 구조를 유추할 수 있는 방법은 존재하지 않는다. 사실 유스케이스는 객체지향과 상관이 없다. ... 단지 기능적 요구사항을 사용자 목표라는 문맥을 중심으로 묶기 위한 정리 기법일 뿐이다.

유스케이스와 객체의 구조 사이에는 커다란 간격이 존재한다.

07 함께 모으기

코드와 모델을 밀접하게 연관시키는 것은 코드에 의미를 부여하고 모델을 적절하게 한다.\
-에릭 에반스(Eric Evans)

마틴 파울러가 설명한 객체지향 설계 안에 존재하는 세 가지 상호 연관된 관점

  • 개념 관점(Conceptual Perspective): 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다. 이 관점은 사용자가 도메인을 바라보는 관점을 반영한다. 따라서 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심이다.
  • 명세 관점(Specification Perspective): 사용자의 영역인 도메인을 벗어나 개발자 영역인 소프트웨어로 초점이 옮겨진다. 이 관점에서 프로그래머는 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점을 맞춘다.
  • 구현 관점(Implementation Perspective): 프로그래머인 우리에게 가장 익숙한 관점으로, 실제 작업을 수행하는 코드와 연관돼 있다. 프로그래머는 객체의 책임을 '어떻게' 수행할 것인 가에 초점을 맞추며 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가한다.

개념 관점, 명세 관점, 구현 관점은 동일한 클래스를 세 가지 다른 방향에서 바라보는 것을 의미한다.

협력을 설계할 때는 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다. 이 말은 메시지를 먼저 선택하고 그 후에 메시지를 수신하기에 적절한 객체를 선택해야 한다는 것을 의미한다.

구현하지 않고 머릿속으로만 구상한 설계는 코드를 구현하는 단계에서 대부분 변경된다. 설계 작업을 위한 스케치를 작성하는 단계지 구현 그 자체일 수 는 없다. 중요한 것은 설계가 아니라 코드다. 따라서 협력을 구상하는 단계에 너무 오랜 시간을 쏟지 말고 최대한 빨리 코드를 구현해서 설계에 이상이 없는지, 설계가 구현 가능한지를 판단해야 한다. 코드를 통한 피드백 없이는 깔끔한 설계를 얻을 수 없다.

객체의 속성이 캡슐화된다는 이야기는 인터페이스에는 객체의 내부 속에 대한 어떤 힌트도 제공돼서는 안 된다는 것을 의미한다. 이를 위한 가장 훌륭한 방법은 인터페이스를 정하는 단계에서는 객체가 어떤 속성을 가지는지, 또 그 속성이 어떤 자료 구조로 구현됐는지를 고려하지 않는 것이다.

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away