본문 바로가기
독서&그 외

'객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향)' 를 읽고

by winteringg 2022. 9. 16.

 어렵고 또 어려운 책이다! 만약 객체 지향을 처음 접한 사람이라면 내용을 이해하기 위해 중간에 멈추지 말고, 처음부터 끝까지 속독하고 나서 다시 한 번 읽어보면 좋을 것 같다.

 

객체지향의 사실과 오해 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com


객체는 무엇인가?
객체란 식별 가능한 개체 또는 사물이다. 
객체는 자동차처럼 만질 수 있는 구체적인 사물일 수도 있고, 시간처럼 추상적인 개념일 수도 있다. 
객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가진다. 
소프트웨어 안에서 객체는 저장된 상태와 실행 가능한 코드를 통해 구현된다.


협력하는 객체들의 공동체

기능을 구현하기 위해 협력하는 객체들

 일상생활에서의 목표는 사람들의 협력을 통해 달성되며, 목표는 더 작은 책임으로 분할되고 책임을 수행할 수 있는 적절한 역할을 가진 사람에 의해 수행된다. 책에서 말하는 예시로 설명해보면 아래 사진과 같은 모양이 된다.

커피를 주문하기 위해 협력하는 사람들

 이 행위들을 객체지향이라는 문맥으로 바꾸어 보면 다음과 같다.

  • 각각의 사람을 객체로 바꾼다.
  • 요청을 메시지로 바꾼다.
  • 요청을 처리하는 방법을 메서드로 바꾼다.

 협력의 핵심은 특정한 책임을 수행하는 객체 역할들간의 연쇄적인 요청과 응답을 통해 목적을 달성한다는 것이다. 이러한 객체 역할들의 특징은 아래와 같다.

  • 여러 객체가 동일한 역할을 수행할 수 있다.
  • 역할은 누구든 대체 가능하다.
  • 각 객체는 책임을 수행하는 방법(메서드 처리 방법)을 자율적으로 선택할 수 있다.
  • 하나의 객체가 동시에 여러 역할을 수행할 수 있다.

협력 속에 사는 객체

 인간의 세계에서 사람이 없으면 역할, 책임, 협력이 아무 의미가 없는 것처럼 객체 지향 세계에서도 협력이 아주 큰 비중을 차지한다. 실제로 협력에 참여하는 주체는 객체이다. 객체가 갖춰야 할 덕목은 이렇다.

  • 객체는 충분히 '협력적'이어야 한다. 외부의 도움 없이 모든 것을 스스로 처리하려고 하는 객체는 그 내부의 복잡도에 의해 자멸할 것이다.
  • 객체는 '자율적'이어야 한다. 객체는 요청이 들어왔을 때 어떤 방식으로 응답할 지 스스로 판단하고 결정할 수 있다. 심지어는 그 요청에 응할지 여부도 스스로 결정한다.

 흔히 객체를 상태(state) 와 행동(behavior)을 함께 지닌 실체라고 정의한다. 객체가 협력에 참여하기 위해 어떤 행동을 해야 한다면 그 행동을 하는 데 필요한 상태여야 한다는 것이다. (ex. 자판기에 금액을 투입하기 전에는 원하는 음료를 선택할 수 없다.) 객체가 자율적인 존재로 남기 위해서는 이렇듯 필요한 행동과 상태를 함께 지녀야 한다. 

 객체의 자율성은 객체의 내부와 외부를 명확하게 구분하는 것으로부터 나온다.

  • 객체의 내부는 객체 스스로 관리하고 외부에서 접근할 수 없도록 차단해야 한다.
  • 객체의 외부에서는 접근이 허락된 경로를 통해서만 객체와 의사소통 할 수 있어야 한다.

 이 객체가 무엇(what)을 처리하는지는 알 수 있지만 어떻게(how) 처리하는지에 대해서는 알 수 없다. 과거의 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분한다. 하지만 객체지향에서는 데이터와 프로세스를 객체라는 하나의 틀 안에 함께 묶어놓음으로써 객체의 자율성을 보장한다. 자율적인 객체로 구성된 공동체는 유지보수가 쉽고 재사용이 용이한 시스템을 구축할 수 있다.

메서드의 자율성

 객체지향 프로그래밍 언어에서 메서드는 클래스 안에 포함된 함수로 구현된다. 어떤 객체에게 메시지를 전송하면 결과적으로 메시지에 대응되는 특정 메서드가 실행된다. 메시지를 수신한 객체가 실행시간에 메서드를 선택할 수 있다는 점은 객체지향 프로그래밍 특징 중 하나이다.

 외부의 요청이 무엇인지 표현하는 메시지와 요청을 처리하기 위한 방법인 메서드의 분리는 객체들간의 자율성을 증진시키는 핵심이다. 이것은 캡슐화 개념과도 관련돼 있다.

객체지향의 본질

 객체지향의 개념을 간략하게 정리하였다.

  • 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로서, 객체를 이용해 시스템을 분할하는 방법이다.
  • 자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임질 수 있는 객체이다.
  • 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다.
  • 각 객체는 협력 내에서 정해진 역할을 수행하며, 역할은 관련된 책임의 집합이다.
  • 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 이 메시지를 수신한 객체는 요청을 처리하기 위한 적절한 메서드를 자율적으로 선택한다.

 객체지향의 핵심은 클래스의 관점을 벗어나 메시지를 주고받는 객체의 관점으로 사고의 중심을 전환하는 것이다. 지나치게 클래스를 강조하는 관점은 객체의 캡슐화를 저해하고 클래스를 서로 강하게 결합시켜버린다. 클래스는 객체들의 협력 관계를 코드로 옮기는 도구에 불과하다. 중요한 것은 어떤 클래스가 필요하느냐가 아닌, 어떤 객체들이 어떤 메시지를 주고받으며 협력하는가 이다.

 

이상한 나라의 객체

객체, 그리고 소프트웨어 나라

 객체는 상태, 행동, 식별자를 지닌다. 왜 상태가 필요한가? 어떤 행동의 결과는 과거에 어떤 행동들이 일어났었느냐에 의존한다. 하지만 과거에 발생한 행동의 이력을 통해 현재 발생한 행동의 결과를 판단하는 것은 번거로우며 복잡하다. 따라서 행동의 과정과 결과를 단순하게 기술하기 위해 상태라는 개념이 고안됐다.

상태와 행동의 연관관계

  • 상태를 이용하면 과거에 얾매이지 않고 현재를 기반으로 객체의 행동 방식을 이해할 수 있다.

저자는 이상한 나라의 앨리스를 예시를 통해 이것을 설명했다.

앨리스의 키를 변화시키는 것 -> 앨리스의 행동
앨리스의 키 -> 상태

 앨리스의 상태를 결정하는 것은 행동이지만 행동의 결과는 상태를 통해 알 수 있다.

앨리스의 키 : 130cm 
케이크를 먹음 : 10cm가 자람
부채질을 함: 5cm가 줄어듬

앨리스의 현재 키 : 135cm
앨리스의 현재 상태를 통해 케이크를 먹고 -> 부채질을 했다는 것을 알 수 있음.

 결국 케이크를 먹거나 부채질을 하기 전의 앨리스의 키가 얼마였느냐가 케이크를 먹거나 부채질을 한 후의 앨리스의 키를 결정한다. 따라서 앨리스가 한 행동의 결과는 앨리스의 상태에 의존적이다.

상태와 프로퍼티

 숫자, 문자열, 양, 속도, 시간, 날짜, 참과 거짓 같은 단순한 값들은 객체가 아니다. 단순한 값들은 그 자체로 독립적인 의미를 가지기 보다는 다른 객체의 상태를 표현하는데에 사용된다. 이렇게 객체의 상태를 구성하는 모든 특징을 통틀어 객체의 프로퍼티라고 한다.

 일반적으로 프로퍼티는 변경되지 않고 고정되기 때문에 '정적'이다. 예를 들어, 동화에서의 앨리스라는 객체는 '키=130cm, 위치=통로' 라는 프로퍼티와 프로퍼티의 값으로 구성되어 있다. 여기서 '키'와 '위치' 라는 프로퍼티는 고정이다. 하지만 그 프로퍼티의 값인 '130cm' 와 '통로' 라는 것은 시간이 흐름에 따라 변경되기 때문에 '동적'이다. 프로퍼티의 값은 다른 말로 '속성(attribute)' 이라고도 한다.

상태와 행동, 캡슐화

 객체는 자율적인 존재라고 위에 언급되었다. 객체지향의 세계에서 한 객체는 다른 객체의 상태에 접근할 수 없다. 자율적인 객체의 상태를 변경하는 것은 객체의 자발적인 행동뿐이다. 객체의 행동으로 인해 발생하는 결과는 두 가지 관점에서 설명할 수 있다.

  • 객체 자신의 상태 변경
  • 행동 내에서 협력하는 다른 객체에 대한 메시지 전송

  행동이란 외부의 요청 또는 수신된 메시지에 응답하기 위한 활동이다. 행동의 결과로 객체는 자신의 상태를 변경하거나 다른 객체에게 메시지를 전달할 수 있다. 객체는 행동을 통해 다른 객체와의 협력에 참여하므로 행동은 가시적이어야 한다. 객체는 상태를 캡슐 안에 감춰둔 채 외부로 노출하지 않는다. 대신 행동을 외부에 노출시키며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동뿐이다. 

외부로 노출하는 행동

  이 그림에서 앨리스와 음료에게 전달되는 메시지 이름을 보면, 이 메시지가 어떤 상태변경을 일으키는지는 외부에서는 전혀 알 수 없다. 바로 이것이 상태는 캡슐 안에 감춰두고 행동을 외부로 노출시키는 것으로 캡슐화가 의미하는 것이다.

식별자

  식별자는 어떤 객체를 다른 객체와 구분하는 데 사용하는 객체의 프로퍼티다. 값은 식별자를 가지지 않기 때문에 상태를 이용한 동등성 검사를 통해 두 값이 같은 지 비교해야 한다. 값의 상태는 결코 변하지 않기 때문에 어떤 시점에 동일한 타입의 두 값이 같다면 언제까지라도 같은 상태를 유지할 것이다. 객체는 상태가 변경될 수 있기 때문에 식별자를 이용한 동일성 검사를 통해 두 인스턴스를 비교해야 한다.

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

행동이 상태를 결정한다.

사람들의 가장 흔한 실수는 협력이라는 문맥을 고려하지 않은 채, 객체가 가져야 할 상태와 행동부터 고민하기 시작한다는 것이다. 
중요한 것은 개별 객체가 아니라 객체들 사이에 이뤄지는 협력이다.
훌륭한 객체지향 설계자는 객체들 간의 요청과 응답 속에서 창발하는 협력에 초점을 맞춰 애플리케이션을 설계한다.
협력이 자리잡으면 저절로 객체의 행동이 드러나고 뒤이어 적절한 객체의 상태가 결정된다.

 상태를 먼저 결정하고 행동을 나중에 결정하는 방법은 설계에 나쁜 영향을 끼친다. 초보자들은 먼저 객체에 필요한 상태가 무엇인지를 결정하고, 그 상태에 필요한 행동을 결정한다. 상태를 먼저 결정하면 안좋은 이유는 아래와 같다.

  • 상태를 먼저 결정할 경우 캡슐화가 저해된다. 상태에 초점을 맞출 경우 상태가 객체 내부로 캡슐화되지 못하고 공용 인터페이스에 그대로 노출되어버릴 확률이 높다.
  • 객체를 협력자가 아닌 고립된 섬으로 만든다. 객체가 필요한 이유는 다른 객체와 협력하기 위해서이다. 하지만 상태에 초점을 맞추면 협력을 무시하고 객체를 만들게 된다.
  • 객체의 재사용성이 저하된다. 객체의 재사용성은 다양한 협력에 참여할 수 있는 능력에서 나온다. 두 번째와 같은 이유로 상태에 초점을 맞춘다면 다양한 협력에 참여할 수 없기 때문에 재사용성이 저하된다.

 객체지향 설계는 애플리케이션에 필요한 협력을 먼저 생각하고, 협력에 필요한 행동을 생각한 후, 그 행동을 수행할 객체를 선택하는 방식으로  설계되어야 한다. 이 과정은 객체가 협력에 참여하게 하여, 응집도 높고 재사용 가능한 객체를 만들 수 있다.

객체의 의인화

 현실 속의 객체와 소프트웨어 객체 사이의 가장 큰 차이점은, 현실 속에서는 수동적인 존재가 소프트웨어 객체로 구현될 때는 능동적으로 변한다는 것이다. 소프트웨어 세계에서 계좌는 스스로 금액을 계산해서 이체 내역을 기록한다. 심지어 현실 속에서는 눈에 보이지도 않는 공기나 바람도 소프트웨어 세계에서는 시각적 형태를 가지도록 만들 수 있다.

 이렇듯 '객체는 현실세계를 모방하는 것' 이라는 조언은 객체지향이 현실의 구조를 따라가야 한다는 오해만 심어준다. 객체지향은 현실 세계의 모방이 아니라 은유를 통한 새로운 세계의 창조이다. 은유란, 실제로 적용되지 않는 개념을 이용해 다른 개념을 서술하는 것이다. 우리는 이미 현실 속의 객체 일부를 소프트웨어 객체로 전달하고 있기 때문에 현실 속의 객체에 대한 은유를 이미 하고 있다. 현실 속의 전화기는 스스로 전화를 걸 수 없지만, 소프트웨어 객체를 통해 현실 전화기 개념을 묘사하면 그 객체가 스스로 전화를 걸 수 있다는 사실을 쉽게 이해하고 기억한다.

 따라서 소프트웨어 객체를 현실 세계의 모방이 아닌 은유라고 생각 한다면, 이를 효과적으로 사용할 경우 이해하기 쉽고 유지보수가 용이한 소프트웨어를 만들 수 있다. 바로 이러한 이유로 현실 세계인 도메인에서 사용되는 이름을 객체에게 부여하라는 가이드가 있는 것이다.

추상화를 통한 복잡성 극복

 추상화는 복잡하고 불필요한 부분을 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법이다. 복잡성을 다루기 위한 추상화는 두 가지 차원에서 이루어진다.

 첫 번째는 구체적인 사물들 간의 공통점을 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것이고, 두 번째는 중요한 부분을 감추기 위해 불필요한 세부사항을 제거함으로써 단순하게 만드는 것이다. 객체지향 패러다임은 객체라는 추상화를 통해 현실의 복잡성을 극복한다.

 하지만 수많은 객체들을 개별적인 단위로 취급하여 추상화 시키기에는 인간이 지닌 인지능력은 턱없이 부족하다. 따라서 공통적인 특성을 기준으로 객체를 여러 그룹으로 묶어 상황을 단순화 시켜야 하는데, 이 객체들을 묶기 위한 그릇을 '개념' 이라고 한다. 일반적으로 우리가 인식하고 있는 다양한 사물이나 객체에 적용할 수 있는 아이디어나 관념을 뜻한다. 보통 길거리에 있는 수많은 나무들을 이야기 할 때, 우리는 개별적으로 하나 하나 이름을 붙여가며 보지 않고 통틀어서 '나무'라고 칭한다. 

 이 개념을 이용하면 객체를 여러 그룹으로 분류할 수 있다. 은행나무, 사과나무, 소나무, 느티나무 등은 '나무' 라는 개념 그룹안에 있다. 이처럼 객체가 어떤 개념 그룹의 일원이 될 때 그 객체를 '인스턴스'라고 한다. 이러한 개념과 분류들은 객체들의 복잡성을 극복하기 위한 추상화 도구이다.

 이 개념이라는 단어를 컴퓨터 공학 차원에서 말하자면 '타입' 이다. 타입은 개념과 완전히 동일하다. 컴퓨터 안의 데이터를 분류하는데 사용하는 개념은 '데이터 타입' 이다. 데이터 타입은 메모리 안에 저장된 데이터의 종류를 분류하는 데 사용하는 메모리 집합에 관한 메타 데이터이다. 타입을 사용하는 이유는 시간에 따라 동적으로 변하는 객체의 복잡성을 단순한 방법으로 구분하기에는 너무 어렵기 때문이다. 이러한 관점에서 타입은 추상화이다. 불필요한 요소를 제거하고 정적인 관점에서 데이터를 바라보게 해줌으로써 객체의 동적인 특징을 추상화할 수 있다.

 

역할, 책임, 협력

 객체의 세계에서 협력이라는 문맥은 객체의 행동 방식을 결정한다. 사람들의 가장 흔한 실수는 협력이라는 문맥을 고려하지 않은 채, 객체가 가져야 할 상태와 행동부터 고민하기 시작한다는 것이다. 중요한 것은 개별 객체가 아니라 객체들 사이에 이뤄지는 협력이다. 훌륭한 객체지향 설계자는 객체들 간의 요청과 응답 속에서 창발하는 협력에 초점을 맞춰 애플리케이션을 설계한다. 협력이 자리잡으면 저절로 객체의 행동이 드러나고 뒤이어 적절한 객체의 상태가 결정된다.

 객체지향 프로그래밍은 객체의 '역할과 책임' 이라는 핵심 가치를 잘 유지할 때 그 의미가 살아난다. 이 역할과 책임의 대한 의의는 객체가 자신의 상태, 즉 정보에 대한 처리책임을 자신 스스로 가진다는 데 있다. 간단히 말해서 객체가 가진 정보는 꺼내서 다른 객체가 처리할 것이 아니라 객체 스스로 처리해야 한다는 것이다. 이 처리에 대한 명령(메소드)을 호출하는 것을 메시지를 전달한다고 표현할 수 있다.

 결국 객체지향 설계란 애플리케이션의 기능을 구현하기 위한 협력 관계를 고안하고, 협력에 필요한 역할과 책임을 식별한 후 이를 수행할 수 있는 적절한 객체를 식별해 나가는 과정이다. 다른 말로 표현하자면 '책임-주도 설계' 라고도 한다. 책임-주도 설계는 개별적인 객체의 상태가 아니라 객체의 책임과 상호작용에 집중한다. 결과적으로 시스템은 스스로 자신을 책임질 수 있을 정도로 충분히 자율적인 동시에 다른 객체와 우호적으로 협력할 수 있을 정도로 충분히 협조적인 객체들로 이루어지게 된다.

테스트 주도 개발

 '테스트-주도 개발'은 애자일 방법론의 한 종류인 XP 의 기본 프랙티스로 소개되면서 주목받기 시작했다. 테스트-주도 개발의 기본 흐름은 실패하는 테스트를 작성하고, 테스트를 통과하는 가장 간단한 코드를 작성한 후 리팩토링을 통해 중복을 제거하는 것이다. 테스트 코드를 통해 작동하는 깔끔한 코드를 얻을 수 있다.

 테스트-주도 개발은 객체가 이미 존재한다고 가정하고 객체에 어떤 메시지를 전송할 것인지에 관해 먼저 생각해야 하는데, 이러한 관점은 역할,책임,협력의 관점에서 객체를 바라봐야 유의미하다. 테스트-주도 개발은 테스트를 작성하는 것이 아니라, 책임을 수행할 객체가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다.

 

책임과 메시지

메시지와 메서드

 객체가 다른 객체에게 접근할 수 있는 유일한 방법은 요청을 전송하는 것 뿐인데, 이 요청을 메시지라고 부른다. 메시지는 객체로 하여금 자신의 책임, 즉 행동을 수행하게 만드는 유일한 방법이다. 하나의 객체는 메시지를 전송함으로써 다른 객체에 접근한다. 메시지를 전송할 때 추가적인 정보가 필요한 경우 메시지의 인자를 통해 추가 정보를 제공할 수 있다.

 이 메시지를 처리하기 위해 내부적으로 선택하는 방법이 바로 메서드이다. 객체는 메시지를 수신하면 해당 메시지를 처리할 수 있는지의 여부부터 확인하고, 처리할 수 있다고 판단되면 그 메시지를 처리할 방법인 메서드를 선택하게 된다. 따라서 어떤 객체에게 메시지를 전송한다는 것은 결과적으로 그 메시지에 대응되는 메서드가 실행된다는 것이다.

다형성

 메시지와 메서드의 차이를 이해하고 나면 객체지향의 핵심 개념인 다형성을 쉽게 이해할 수 있다. 다형성이란 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응하는 것을 말한다. 좀 더 구체적으로 말하자면 서로 다른 타입에 속하는 객체들이 동일한 메시지를 수신할 경우 서로 다른 메서드를 이용해 메시지를 처리할 수 있는 메커니즘을 가리킨다. 

 메시지는 '무엇'이 실행될지는 명시하지만 '어떻게' 실행할 지는 전적으로 메시지를 받는 수신자가 결정한다. 메시지에는 처리 방법과 관련된 어떠한 제약도 없기 때문에 동일한 메시지라고 하더라도 서로 다른 방식의 메서드를 이용해 처리할 수 있다. 서로 다른 객체들이 다형성을 만족시킨다는 것은 객체들이 동일한 책임을 공유하는 것을 의미한다. 다형성은 동일한 역할을 수행할 수 있기 때문에 객체들 사이의 대체 가능성을 의미한다. 이러한 대체 가능성을 이용해 설계를 유연하고 재사용 가능하게 만든다. 

 다형성을 사용하면 송신자가 수신자의 종류를 모르더라도 메시지를 전송할 수 있다. 즉 다형성은 수신자의 종류를 캡슐화한다. 

송신자와 수신자를 연결하는 것은 메시지

 얼핏 보면 다형성이 모든것을 가능하게 하는 것 같지만 사실 이 모든것은 다형성을 지탱하는 메시지가 존재하기 때문에 가능한 것이다. 송신자는 오직 메시지만 바라본다. 수신자의 정확한 타입을 모르더라도 상관 없고, 단지 수신자가 메시지를 이해하고 처리해 줄 것이라는 사실만 알아도 충분하다.

 객체지향의 기본 개념은 책임을 수행하는 자율적인 객체들의 협력을 통해 애플리케이션을 구축하는 것이다. 객체지향 세계에서 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 방법은 메시지를 전송하는 것이다. 다른 객체와  협력할 필요가 있는 객체는 메시지를 전송하고, 메시지를 수신한 객체는 미리 정의된 방법에 따라 수신된 메시지를 처리한다. 

 훌륭한 객체지향 설계는 어떤 객체가 어떤 메시지를 전송할 수 있는가와 어떤 객체가 어떤 메시지를 이해할 수 있는가를 중심으로 객체 사이의 협력 관계를 구성하는 것이다. 

인터페이스

 인터페이스란 어떤 두 사물이 마주치는 경계 지점에서 서로 상호작용할 수 있게 이어주는 방법이나 장치이다. 개발자들은 미리 약속된 애플리케이션 프로그래밍 인터페이스를 통해 다른 사람이 작성한 코드와 상호작용한다. 인터페이스는 다음과 같은 특징을 지닌다.

1) 인터페이스의 사용법을 익히기만 하면 내부 구조나 동작 방식을 몰라도 쉽게 대상을 조작하거나 의사를 전달할 수 있다.
 - 예를 들어 우리는 TV 리모콘의 내부 구조와 동작 원리를 알지 못해도 리모콘을 사용하는 것에는 아무런 지장이 없다.

2) 인터페이스 자체는 변경하지 않고 단순히 내부 구성이나 작동 방식만을 변경하는 것은 사용자에게 어떠한 영향도 미치지 않는다.
 - 리모콘의 내부 구조를 변경한다고 해서 리모콘을 사용하는 방법이 변하는 것은 아니다.

3) 대상이 변경되더라도 동일한 인터페이스만 제공하면 아무런 문제없이 상호작용할 수 있다.
 - 어떤 리모콘 인터페이스에 능숙하다면 다른 종류의 TV 리모콘이 있어도 무난히 사용할 수 있다.

 협력에 참여하는 객체 역시 동일하게 다른 객체와 상호작용 한다. 그리고 위의 예시처럼 객체의 인터페이스만 알면 객체의 내부 구조나 작동 방식을 몰라도 객체와 상호작용할 수 있다. 또한 인터페이스만 유지된다면 객체의 내부 구조나 작동 방식을 변경하거나 다른 객체로 대체한다고 하더라도 인터페이스 사용자에게는 영향을 미치지 않는다.

 객체가 다른 객체와 상호작용할 수 있는 유일한 방법은 메시지 전송이다. 따라서 객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성되며 객체가 어떤 메시지를 수신할 수 있는지가 객체가 제공하는 인터페이스의 모양을 가진다.

 훌륭한 객체란 구현을 모른 채 인터페이스만 알면 쉽게 상호작용 할 수 있는 객체를 의미한다. 이것은 객체를 설계할 때 객체 외부에 노출되는 인터페이스와 객체의 내부에 숨겨지는 구현을 명확하게 분리해서 고려해야 한다는 것을 의미한다. 이를 인터페이스와 구현의 분리 원칙이라고 한다. 객체가 가져야 할 상태와 메서드 구현은 객체 내부에 속한다. 이 부분을 수정하더라도 객체 외부에 영향을 미쳐서는 안 된다. 객체 외부에 영향을 미치는 변경은 객체의 공용 인터페이스를 수정할 뿐이다.

캡슐화

 객체의 자율성을 보존하기 위해 구현을 외부로부터 감추는 것을 캡슐화라고 한다. 객체는 상태와 행위를 함께 캡슐화함으로써 충분히 협력적이고 만족스러울 정도로 자율적인 존재가 될 수 있다. 캡슐화를 정보 은닉이라고 부르기도 한다. 객체지향에서 캡슐화는 두 가지 관점으로 사용된다.

1) 상태와 행위 관점의 캡슐화
 - 상태와 행위의 조합인 객체는, 스스로 자신의 상태를 관리하며 상태를 변경하고 외부에 응답할 수 있는 행동을 내부에 보관한다. 객체는 상태와 행위를 한 데 묶은 후 외부에서 반드시 접근해야만 하는 행위만 골라 공용 인터페이스를 통해 노출한다. 그리고 객체가 자율적이기 위해서는 자기 자신의 상태를 스스로 관리할 수 있어야 한다. 따라서 캡슐화는 인터페이스와 구현을 분리하기 위한 전제 조건이며 자율적인 객체를 만들기 위한 전제 조건이기도 하다.

2) 사적인 비밀 관점의 캡슐화
 - 객체는 외부의 객체가 자신의 내부 상태를 직접 관찰하거나 제어할 수 없도록 막기 위해 의사소통 가능한 특별한 경로만 외부에 노출한다. 이처럼 외부에서 객체와 의사소통할 수 있는 고정된 경로를 공용 인터페이스라고 한다. 객체의 공용 인터페이스는 외부에서 전송 가능한 메시지의 집합이다. 외부 객체는 오직 공용 인터페이스에 정의된 메시지를 통해서만 객체에 접근할 수 있다. 불필요한 공격이나 간섭으로부터 내부 상태를 격리함으로써 객체는 공용 인터페이스를 경계로 최대한의 자율성을 보장받을 수 있다.

 

객체 지도

도메인 모델

 소프트웨어를 사용하는 사람들은 자신이 관심을 가지고 있는 특정 분야의 문제를 해결하기 위해 소프트웨어를 사용한다. 이처럼 사용자가 프로그램을 사용하는 대상 분야를 도메인이라고 한다. 도메인 모델은 지식을 선택적으로 단순화하고 의식적으로 구조화한 형태이다. 즉, 대상을 추상화하고 단순화한 것이다. 모델을 사용하면 현재의 문제와 관련된 측면은 추상화하고 그 밖의 관련 없는 세부 사항에 대해서는 무시할 수 있다. 도메인은 복잡성을 관리하기 위해 사용하는 기본 도구이다.

 최종 코드는 사용자가 도메인을 바라보는 관점을 반영해야 한다. 이것은 곧 애플리케이션이 도메인 모델을 기반으로 설계돼야 한다는 것을 의미한다. 도메인 모델이란 사용자들이 도메인을 바라보는 관점이며, 설계자가 시스템의 구조를 바라보는 관점인 동시에 소프트웨어 안에 구현된 코드의 모습 그 자체이다. 

 안정적인 도메인 모델을 기반으로 시스템의 기능을 구현할 경우 시스템의 기능이 변경 되더라도 비즈니스의 핵심 정책이나 규칙이 변경되지 않는 한 전체적인 구조가 한 번에 흔들리지는 않는다. 이것이 일반적으로 객체지향이 기능의 변경에 대해 좀 더 유연하게 대응할 수 있는 패러다임이라고 하는 이유이다.

객체지향에 존재하는 세 가지 관점

1) 개념 관점
 - 개념 관점에서 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다. 도메인이란 사용자들이 관심을 가지고 있는 특정 분야나 주제이기 때문에 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심이다.

2) 명세 관점
 - 명세 관점에서는 사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨진다. 명세관점은 도메인의 개념이 아니라 실제로 소프트웨어 안의 객체들의 책임에 초점을 맞추게 된다. 즉 객체의 인터페이스를 바라보게 된다. "구현이 아니라 인터페이스에 대해 프로그래밍 하라[GOF 1994]"를 따르는 것은 명세 관점과 구현 관점을 명확하게 분리하는 것에서부터 시작된다.

3) 구현 관점
 - 구현 관점은 프로그래머인 우리에게 가장 익숙한 관점으로 실제 관점을 수행하는 코드와 연관돼 있다. 구현 관점의 초점은 객체들의 책임을 수행하는 데 필요한 동작하는 코드를 작성하는 것이다. 따라서 프로그래머는 객체의 책임을 어떻게 수행할 것인가에 초점을 맞추며 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가한다.

'독서&그 외' 카테고리의 다른 글

'새로운 CSS 레이아웃' 을 읽고  (1) 2022.09.30
'웹디자이너를 위한 HTML5' 를 읽고  (0) 2022.09.28
git pull 지양하기  (0) 2022.08.18
Git-flow, GitHub-flow 정리  (0) 2022.08.16
IntelliJ 단축키 for Mac OS  (0) 2022.08.16

댓글