일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 스프링 리액티브 프로그래밍
- 문자형을 날짜형으로
- str_to_date
- batchInsert
- 마이바티스 트랜잭션
- git stage
- ChainedTransactionManager #분산데이터베이스 #Spring Boot #MyBatis
- jar 소스보기
- JSON 분리
- JSONObject 분할
- spring reactive programming
- 성능개선
- JobExecutionAlreadyRunningException
- JSONArray 분할
- 폐기하기
- spring webflux
- 날짜형을 문자형으로
- 스프링 배치 공식문서
- Meta Table
- nonblocking
- JSON 분할
- 스프링 배치 메타 테이블
- 마리아디비
- JSON 분해
- 무시하기
- date_format
- 스프링 웹플럭스
- org.json
- multi update
- 스테이지에 올리기
- Today
- Total
ebson
[개발 서적] '객체지향의 사실과 오해' 미리보기 본문
'객체지향의 사실과 오해'를 읽게된 계기
좋은 기회로 현업에 계신 개발자분께 개발 서적을 몇권 추천받았다. 조영호 님의 '오브젝트(2019)'와 '객체지향의 사실과 오해(2015)' 가 그것이다. 먼저 쓰였으면서 더 적은 분량의 '객체지향의 사실과 오해'를 먼저 읽어봤다. 일반적으로 개발 서적을 구매할 때는 목차와 서평을 참고하는 것으로 충분할 수 있지만, 당장 실무에 적용할 수 있는 내용이 아니라고 판단될 경우에는 좀 더 구체적인 내용 요약이 담긴 서평을 찾는 편이다. 그래서 며칠간 '객체지향의 사실과 오해'를 정독하고 재차 훑어본 것을 바탕으로 핵심 문장들을 추린 것과 함께 읽고 나서 느낀점을 적어보려고 한다.
조영호 님의 '객체지향의 사실과 오해(2015)'는 객체지향 프로그래밍에 대한 일반적인 이해에 대한 저자의 관점을 전달하기 위해 객체지향의 핵심 개념들을 대중적인 예시와 함께 알기 쉽게 설명하는 책이다. 매일 아침 직장인들이 카페에서 보게되는 내용들을 객체지향 프로그래밍 관점에서 해석하는 것으로 객체의 역할, 책임, 협력의 개념을 설명한다. '객체지향 프로그래밍은 실세계의 모방이라기보다는 오히려 새로운 세계의 창조다' 라는 저자의 관점을 설명하기 위해 '이상한 나라의 앨리스'를 예로 들어 객체의 상태, 행동, 식별자, 타입과 추상화와 같은 객체지향 프로그래밍의 핵심 개념들을 설명한다.
이상의 내용들과 함께 책임-주도 설계, 책임과 메시지, 메시지와 메서드, 인터페이스, 유스케이스, 도메인 모델과 같은 키워드를 중심으로 6장까지 다루었던 내용들을 바탕으로 7장에서는 1장에서 다루었던 카페 이야기를 구체적인 자바 코드와 함께 다시 설명하면서 저자의 관점에서의 객체지향 프로그래밍에 대한 이해를 확실히 할 수 있게 돕는다. 그리고 추상화 기법에 대한 부록이 있다.
아래부터는 각 장마다 핵심 문장들을 추려서 정리한 것이다. 물론 책을 직접 구입해 읽는 것보다 글의 흐름이 부자연스럽고 이해하기 어려울 것이다. 단락의 구분도 미흡하다. 그럼에도 불구하고 목차보다는 구체적인 내용을 원하는 분들에게 도움이 되었으면 좋겠다.
01 협력하는 객체들의 공동체
객체지향이 실세계의 모방이라는 개념은 객체지향을 설명하기에 유용하지만 적합하지는 않다. 객체지향의 목표는 실세계의 모방이라기보다는 오히려 새로운 세계를 창조하는 것이기 때문이다. 객체를 현실 시계의 생명체에 비유하는 것은 상태와 행위의 '캡슐화'와 소프트웨어 객체의 '자율성'을 설명하고 '메시지'를 주고 받으며 협력하는 객체들의 관계를 설멍하는 것에 유용하다. 그러나 이것은 훌륭한 객체지향 프로그래밍을 구현하는 실무적인 관점에서는 부적합하다.
예를 들어, 매일 아침 직장인이 커피를 주문하는 장면을 떠올려보자. 모든 음료 주문은 손님이 커피를 주문하고, 캐시어가 주문을 받고, 바리스타가 커피를 제조하는 과정에서 역할, 책임, 협력이 조화를 이루며 완료된다. 역할, 책임, 협력은 객체지향의 가장 중요한 개념이다. 음료 주문이라는 일상의 문제는 커피를 주문하라는 손님의 요청과 캐시어의 응답, 커피를 제조하라는 캐시어의 요청과 바리스타의 응답으로 구성된 협력으로 이해할 수 있다. 역할은 협력에 참여하는 특정한 사람이 협력 안에서 가지는 책임이나 의무이다. 특정한 역할은 특정한 책임을 암시한다. 이때, 역할은 여러 사람이 대체할 수 있고 그들은 책임을 수행하는 방법을 자율적으로 선택할 수 있다. 그리고 한 사람이 동시에 여러 역할을 수행할 수도 있다.
이와 같이 실세계의 커피를 주문하는 과정은 객체지향의 핵심적인 내용들을 포함한다. 사람을 객체로, 요청을 메시지로, 요청을 처리하는 방법을 메서드로 바꾸면 객체지향이라는 문맥으로 옮겨올 수 있다. 한편, 협력의 핵심은 특정한 책임을 수행하는 역할들 간의 연쇄적인 요청과 응답으로 목표를 달성하는 것이다. 애플리케이션의 기능은 더 작은 책임으로 분할되고 책임은 역할을 수행하는 객체에게 부여된다. 객체에게 적절한 책임을 부여하는 것은 좋은 품질의 객체지향 설계에 중요한 요소이다. 커피 주문 과정으로 돌아와서, 역할은 캐시어나 바리스타가 협력에 참여하면서 갖는 일종의 페르소나이다. 역할은 관련성 높은 책임의 집합이다.
협력에 참여하는 주체인 객체는 애플리케이션의 기능을 구현하기 위해 존재다. 협력 공동체의 일원으로서 객체는 첫째, 협력적이어야 하고 둘째, 자율적이어야 한다. 다른 객체의 요청에 따라 응답할 수 있으면서 그 절차와 방법을 스스로 결정할 수 있어야 한다. 그래서 객체를 흔히 '상태와 행동을 함께 지닌 자율적인 객체다' 라고 정의한다. 객체의 자율성은 객체의 내부와 외부를 명확히 구분하는 것에서 나온다. 예를 들어, 커피 주문 과정에서 손님과 캐시어, 바리스타는 요청과 응답을 주고 받는 과정에서 스스로 생각하고 판단하는 자율적인 존재이다. 객체는 자신의 상태를 직접 관리하고 행위를 결정할 수 있는 존재이다. 데이터와 프로세스를 엄격하게 구분하는 전통적인 개발 방법에 반해 객체지향에서는 데이터와 프로세스를 객체라는 틀 안에 두면서 객체의 자율성을 보장한다.
커피 주문 과정에서 손님, 캐시어, 바리스타가 자신의 책임을 다하기 위해 요청하고 응답하듯 객체지향 세계에서는 객체들이 협력하기 위해 메시지를 주고 받는다. 객체가 수신된 메시지를 처리하는 방법은 메서드라고 부른다. 객체는 수신한 메시지에 대응하는 메서드를 실행한다. 메세지와 메서드의 분리는 객체의 자율성을 증진시킨다. 예를 들어, 바리스타는 커피 머신으로 커피를 제조할 수도 있고 수작업으로 커피를 제조할 수도 있다. 메시지와 메서드를 분리하는 것은 캡슐화 개념과도 관련이 있다.
결론적으로, 객체지향이란 시스템을 자율적인 객체들의 협력 공동체로 보는 관점에 관한 것이다. 자율적인 객체를 상태와 행위를 가지며 스스로 책임을 수행한다. 객체는 다른 객체와 협력하기 위해 책임의 집한인 역할을 수행한다. 객체는 메시지를 송신하고 수신하며 메시지를 처리하기 위한 메서드를 실행한다.
한편, 많은 사람들이 객체지향 개념에 대하여 클래스를 가장 먼저 떠올리는 경향이 있다. 클래스는 객체지향의 중심 개념이 아니다. 훌륭한 객체지향 설계를 위해서 코드를 담는 클래스 관점에서 메시지를 주고 받는 객체의 관점으로 사고를 전환해야 한다. 객체지향의 핵심은 클래스가 아니다. 객체지향의 핵심은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력관계를 구축하는 것이다. 그러므로 클래스의 구조와 메서드가 아니라 객체의 역할, 책임, 협력에 집중하여야 한다.
02 이상한 나라의 객체
객체지향 패러다임은 일반적인 인간의 인지능력에 대한 이해에서 비롯되었다. 인간은 세상을 식별 가능하고 독립적인 객체들의 집합으로 바라본다. 객체지향은 세상을 자율적이고 독립적인 객체들의 집합으로 이해하는 인간의 인지능력에 기반을 두고 있다. 인간의 인지 능력은 물리적인 것 뿐만 아니라 추상적인 것 까지도 객체로 인식할 수 있다. 세상을 더 작은 객체로 분해하는 것은 인간이 세상이 가진 복잡성을 극복하려는 노력에서 기인한다. 객체지향 패러다임도 역시 한 인간이 인지할 수 있는 더 작은 단위의 객체들로 소프트웨어가 이루어져있다는 믿음에서 출발한다.
그러나 객체지향의 목적은 현실 세계를 모방하는 것이 아니라 현실 세계를 기반으로 새로운 세계를 창조하는 것이다. 예를 들어, 현실 세계의 전등은 사람의 손이 없이는 불을 밝힐 수 없으나 소프트웨어 세계의 전등은 스스로 불을 밝힐 수 있다. 그래서 객체지향 애플리케이션의 겉모습은 현실 세계와 유사해보이지만 내부를 들여다보면 매우 이질적인 모습이다.
이상한 나라의 앨리스 이야기를 바탕으로 객체지향을 이해해보자. 앨리스는 문을 통과하거나 음식을 먹어서 자신의 키를 늘리거나 줄인다. 앨리스의 상태를 결정하는 것은 앨리스의 행동이고 행동의 결과를 결정하는 것은 상태이다. 앨리스의 행동의 성공 여부는 이전에 어떤 행동들이 발생했는지에 영향을 받는다. 그리고 앨리스는 어떤 상태에 있더라도 유일하게 식별 가능하다.
객체의 특징은 인간의 인지 능력과 그 한계에서 비롯된다. 유일하게 식별 가능한 어떤 것이든 객체가 될 수 있다. 객체는 상태, 행동, 식별자를 지닌 실체다. 객체가 외부와 상호작용한 결과는 그 시점까지 객체에 발생한 일에 의존적이다. 예를 들어, 비행기를 탑승하기 전에 먼저 항공권을 발권해야 한다. 앨리스의 이야기로 돌아와서, 앨리스가 문을 통과하기 전에는 먼저 부채질을 하거나 버섯을 먹어서 자신의 키를 줄여야 한다. 이처럼 상태를 이용해 과거의 모든 행동 이력을 설명하지 않아도 행동의 결과를 예측할 수 있다. 상태는 세상의 복잡성을 완화하고 인지 과부하를 줄일 수 있게 한다.
객체를 이해하려면 객체의 상태와 행동의 관계를 이해해야 한다. 모든 객체의 상태는 단순한 값과 객체의 조합으로 표현할 수 있다. 객체의 상태를 구성하는 특징을 프로퍼티라고 한다. 앨리스의 키, 위치, 음료는 앨리스의 프로퍼티다. 객체와 객체 사이의 의미 있는 연결을 링크라고 한다. 객체는 링크를 통해서 메시지를 주고 받을 수 있다. 앨리스의 위치 프로퍼티 값이 통로라면 음료와 링크가 있지만, 앨리스의 위치 프로퍼티 값이 정원이라면 음료와 링크가 없다. 객체를 구성하는 단순한 값은 속성이라고 한다. 앨리스의 키와 위치는 속성이다. 프로퍼티는 속성과 링크의 조합으로 표현할 수 있다. 객체는 다른 객체의 상태에 접근하거나 다른 객체의 상태를 변경할 수 없다. 객체는 행동을 통해 간접적으로 객체의 상태를 변경하는 것을 가능하게 한다. 객체는 스스로의 행동에 의해서만 상태가 변경되는 자율적인 존재이다.
이와 같은 상태의 캡슐화를 통한 객체의 자율성은 질좋은 협력을 가능하게 한다. 객체 자신의 자율적인 행동에 의해서만 상태가 변경될 수 있다. 객체의 행동은 상태의 영향을 받는다. 상태를 사용해 복잡한 객체의 행동을 쉽게 이해할 수 있다. 객체는 서로 협력하면서 자신의 상태를 변경시킬 뿐만 아니라 메시지를 전송하면서 다른 객체의 상태의 변경을 유발할 수 있다. 객체지향 세계에서 모든 객체는 자신의 상태를 스스로 관리한다. 객체는 상태를 캡슐 안에 감춰두고 관리한다. 캡슐화는 결과적으로 객체의 자율성을 높이고 협력을 단순하고 유연하게 만든다.
모든 객체는 식별자를 가진다. 값이 같은지 여부는 상태가 같은지 여부로 판단한다. 상태는 시간에 흐름에 따라 변하기 때문에 상태를 기반으로 객체를 식별할 수 없다. 객체는 상태 변경에 독립적인 식별자를 가진다. 참조 객체, 엔티티는 식별자를 지닌 전통적인 의미의 객체를 가키리는 용어이고 값 객체는 식별자를 가지지 않는 값을 가리키는 용어이다.
현실 속의 객체와 소프트웨어 객체 사이의 가장 큰 차이점은 현실에서 수동적인 객체가 소프트웨어에서는 능동적으로 행동할 수 있는 객체가 된다는 것이다. 예를 들어, 현실 속의 전화기는 스스로 통화 버튼을 누를 수 없는 반면 소프트웨어의 전화기는 스스로 통화 버튼을 누를 수 있다. 이러한 소프트웨어 객체의 특징을 의인화라고 한다. 그리고 현실 속의 객체의 의미 일부가 소프트웨어 객체로 전달되는 것은 은유라고 할 수 있다. 은유 관계에 있는 실체 객체의 이름을 소프트웨어 객체의 이름으로 사용함으로서 표현적 차이를 줄이고 이해하기 쉬우며 유지보수가 용이한 소프트웨어를 만들 수 있다. 그래서 현실 세계의 도메인에서 사용되는 이름을 객체에게 부여하는 것이다.
이처럼 객체지향 설계의 목적은 현실세계를 모방하는 것이 아니라 새로운 세계를 창조하는 것이다. 현실 속의 객체를 바탕으로 은유를 통해 이상한 나라의 객체를 새롭게 창조하는 것이다.
03 타입과 추상화
추상화는 현실에서 불필요한 부분을 제외하고 본질을 부각하면서 현실의 복잡성을 극복하고 더 쉽게 이해할 수 있도록 돕는다. 추상화는 복잡한 현실을 단순화하기 위한 인간의 인지 수단 중 하나이다. 객체지향은 객체라는 추상화를 통해 현실세계의 복잡성을 극복한다.
예를 들어, 이상한 나라의 앨리스에 등장하는 트럼프들은 그 유사성을 기반으로 추상화된 객체이다. 이상한 나라의 앨리스에 등장하는 다양한 인물들이 어떤 유사성에 의해 트럼프라는 객체로 추상화되어 불려진다. 이것은 앨리스가 정원을 바라보는 정원에 내재된 복잡성을 효과적으로 감소시킨다. 앨리스가 인물들의 차이점은 무시하고 공통점만을 취해 트럼프라는 개념으로 단순화했기 때문이다. 공통점을 기반으로 객체들을 묶기 위한 그릇을 개념이라고 한다. 개념을 이용해 객체를 그룹들로 분류할 수 있다. 어떤 객체가 어떤 개념을 적용할 수 있어서 그 그룹의 일원이 될 때 그 객체를 그 개념의 인스턴스라고 한다.
개념은 세가지 관점으로 설명할 수 있다. 개념을 가리키는 이름인 심볼, 객체가 개념에 속하는지 여부를 확인하는 내연, 개념에 속하는 모든 객체의 집합인 외연이 그것이다. 예를 들어, 트럼프라는 개념의 심볼은 트럼프, 내연은 납작하고 네모남, 외연은 정원사, 병사, 신하, ... 왕과 왕비들, 하트왕과 하트 여왕이다. 한편, 분류는 객체지향의 주요 개념 중 하나이다. 어떤 객체를 어떤 개념으로 분류할지는 객체지향의 품질에 영향을 미친다. 사물들간 공통점은 취하고 차이점은 버리는 일반화와 세부사항을 제거해 중요한 부분을 강조하는 단순화를 통한 추상화의 두가지 차원을 모두 사용해 객체를 분류한다. 현실 세계의 무수히 많은 존재들을 개념의 틀로 분류하며 추상화 함으로서 복잡성을 완화할 수 있다.
타입은 개념이다. 타입은 공통점을 기반으로 객체들을 묶는 틀이다. 타입은 개념처럼 심볼, 내연, 외연으로 설명할 수 있고 타입에 속하는 객체를 타입의 인스턴스라고 한다. 메모리 안의 0과 1로 이루어진 무수히 많은 혼란스러운 데이터들을 목적에 따라 분류하기 시작하면서 프로그래밍 언어에는 타입 시스템이 생겨났다. 타입은 데이터가 어떻게 사용되느냐에 관한 것이다. 그리고 타입에 속한 데이터를 메모리에 표현하는 방식은 외부로부터 감춰진다. 객체가 수행하는 행동이 객체가 속하는 타입을 결정하며 객테의 내부적인 표현은 외부로부터 감춰진다. 예를 들어, 앨리스가 트럼프라는 타입으로 분류한 존재들은 그들이 동일한 행동 방식을 취했기 때문이다.
한편, 트럼프와 트럼프 인간의 관계에서 볼수 있는 것은 타입과 타입 사이에 존재하는 일반화/특수화 관계이다. 일반화와 특수화는 동시에 일어난다. 트럼프 인간은 트럼프를 더 특수하게 표현하고 트럼프는 트럼프 인간보다 범위가 더 넓은 개념이다. 객체지향에서 일반화/특수화 관계를 결정하는 것은 객체의 상태를 표현하는 데이터가 아니라 행동이다. 일반적인 타입은 특수한 타입이 가진 모든 행동 중에서 일부 행동만 가진다. 일반화/특수화 관계에서 일반적인 타입은 특수한 타입보다 더 적은 수의 행동을 가지고 더 큰 크기의 외연 집합을 가진다.
일반화/특수화 관계에서 더 일반적인 타입을 슈퍼타입이라고 하고 더 특수한 타입을 서브타입이라고 한다. 일반화/특수화 계층은 객체의 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거하는 추상화의 차원을 적절히 활용한 예이다. 예를 들어, 앨리스가 트럼프 인간들의 걸을 수 있는 특성은 배제하고 단지 트럼프로 생각한 것은 트럼프의 특성에만 집중하여 상황을 단순화한 결과이다. 앨리스는 다양한 등장 인물들을 공통의 타입인 트럼프 인간으로 분류하는 한편 트럼프의 특성에만 집중해 일반화했다. 이처럼 객체지향 패러다임에서는 분류와 일반화/특수화 기법을 동시에 적용하게 된다.
타입은 추상화이다. 타입을 이용해 객체의 동적인 특성을 추상화한다. 추상화는 시간의 변화에 따른 객체의 상태 변경이라는 복잡성을 단순화하는 방법이다. 객체가 특정 시점에 가지는 구체적인 상태를 객체의 스냅샷 또는 객체 다이어그램이라고 한다. 이것을 동적 모델이라고 한다. 객체가 가질 수 있는 모든 상태와 모든 행동을 시간에 독립적으로 표현하는 모델은 타입 모델 또는 정적 모델이라고 한다. 객체지향 설계에서는 객체 관점의 동적 모델과 객체를 추상화한 타입 관점의 정적 모델을 모두 사용해야 한다.
한편, 객체지향 프로그래밍 언어에서 정적인 모델은 클래스로 구현된다. 그러나 클래스는 타입과 동일한 개념은 아니다. 객체를 분류하는 기준은 타입이고 타입을 나누는 기준은 객체가 수행하는 행동이다. 클래스는 타입을 구현하기 위해 프로그래밍 언어에서 제공하는 구현 메커니즘이다.
04 역할, 책임, 협력
인간의 행동을 결정하는 것은 인간의 본질적인 특성이라기보다는 오히려 개인이 처해 있는 정황 또는 문맥일 가능성이 더 크다. 인간의 행동을 결정하는 문맥은 타인과의 협력이다. 객체의 세계에서도 협력이라는 문맥이 객체의 행동을 결정한다. 객체지향 설계의 품질을 결정하는 것은 역할, 책임, 협력이라는 개념이다.
현실세계에서 이뤄지는 협력의 본질은 요청과 응답으로 연결되는 네트워크이다. 객체지향 세계는 동일한 목적을 달성하기 위해 협력하는 객체들의 공동체이다. 앨리스의 이야기에서 왕은 재판을 요청받아 책임을 수행하기 위해 토끼에게 목격자를 요청하고 모자장수에게 증언을 요청한다. 이들은 각자 요청에 대한 지식과 행동 방식을 갖고 있기 때문에 요청을 받는다. 요청과 응답은 협력에 참여하는 객체가 수행할 책임을 정의한다.
객체지향 세계에서 협력에 참여하는 객체들은 목표 달성을 위해 필요한 책임을 수행한다. 객체의 책임은 객체가 무엇을 알고 있는가와 무엇을 할 수 있는가로 구성된다. 아는 것의 측면은 외부에 제공해 줄 수 있는 정보이고 하는 것의 측면은 외부에 제공해 줄 수 있는 서비스이다. 책임은 객체의 공용 인터페이스를 구성한다.
객체는 협력을 통해 다른 객체로부터 요청을 수신한 경우에만 책임을 수행한다. 객체는 다른 객체가 책임을 수행하도록 메시지를 전송한다. 메시지는 협력에 참여하는 객체 사이의 관계를 강조한 것이다. 그러나 책임과 메시지의 수준이 같은 것은 아니다. 책임은 객체가 수행할 행위를 상위 수준에서 서술한 것이다. 하나의 책임은 여러 메시지로 분할될 수 있다. 객체지향 설계는 협럭에 참여하는 객체가 어떤 책임을 수행하고 어떤 객체로부터 어떤 메시지를 수신할 것인지 결정하는 것에서 시작한다.
객체가 수행하는 책임들은 객체가 협력 안에서 수행하는 역할에 관한 것이다. 역할은 유연성과 재사용성이 있는 객체지향 설계를 함에 있어서 중요한 요소이다. 역할의 개념을 사용해 유사한 협럭을 추상화할 수 있다. 같은 객체들이 다양한 협력에 참여할 수 있고 다양한 객체들이 동일한 협력에 참여할 수 있다. 역할은 객체지향 설계의 유연성, 재사용성, 단순성을 뒷받침하는 핵심 개념이다. 예를 들어, 앨리스 이야기의 재판이라는 협력과정에서 등장하는 객체들은 모두 이들 각 객체는 재판이 아닌 다른 협력에 참여할 수도 있다. 재판이라는 협력 과정을 보자면, 하트 왕뿐만 아니라 하트 여왕도 판사의 역할을 수행할 있고 모자 장수와 요리사, 그리고 앨리스가 증인의 역할을 수행할 수 있다.
역할은 하나의 협력 안에 여러 종류의 객체가 참여할 수 있게 하면서 협력을 추상화 할 수 있다. 역할은 협력 안에서 구체적인 객체로 대체될 수 있는 추상적인 협력자이다. 역할을 대체하는 객체들간의 행동은 호환될 수 있다. 객체는 역할에 주어진 책임 외에 다른 책임도 수행할 수 있다. 객체는 역할이 암시하는 책임 이상의 책임을 가질 수 있다. 역할은 객체의 추상화이기 때문이다. 역할의 대체 가능성은 행위 호환성을 의미하고, 행위 호환성은 동일한 책임의 수행을 의미한다.
객체는 시스템의 데이터를 저장하기 위한 자료구조라기 보다는 행위를 수행하면서 협력에 참여하기 위한 존재이다. 그리고 객체지향은 클래스간의 관계를 표현하는 시스템의 정적인 측면에 중점을 두는 것이 아니라 협력에 참여하는 객체의 동적인 측면에 중점을 둔다. 그래서 올바른 객체를 설계하려면 먼저 견고하고 깔끔한 협력을 설계해야 한다. 객체에게 책임을 할당하면서 외부에 제공하는 행동이 결정된다. 행동을 결정하면 객체가 필요로 하는 데이터를 정의할 수 있다. 객체의 행위에 초점을 맞추기 위해서 객체간의 협력이라는 문맥 안에서 적절하게 책임을 부여해야 한다. 객체를 충분히 협력적으로 만들고 협력 안에서 객체를 충분히 자율적으로 만들어야 한다.
이처럼 역할, 책임, 협력은 좋은 객체지향 설계를 위한 토양이다. 책임-주도 설계는 협력에 필요한 책임들을 식별하고 객체에게 할당하는 설계 방법이다. 시스템의 기능을 더 작은 규모의 책임으로 분할하고 적절한 객체에게 할당한다. 객체는 책임을 수행하기 위해 다른 객체의 도움이 필요한 경우 객체로부터 정보나 서비스를 요청하기 위해 객체들간의 협력 공동체를 구축한다. 그리고 디자인 패턴은 책임-주도 설계의 결과를 표현하는 것이다. 디자인 패턴은 반복적으로 발생하는 문제와 그에 대한 해법의 쌍으로 정의된다. 디자인 패턴은 공통으로 사용할 수 있는 역할, 책임, 협력에 대한 템플릿이며 책임-주도 설계의 지름길이라고 할 수 있다.
한편, 테스트-주도 개발은 책임-주도 설계의 기본 개념을 따르면서 객체지향에 대한 깊은 지식을 요구하는 개발 방법이다. 좋은 객체지향 설계에 대한 경험을 바탕으로 협력 관계 속에서 객체들의 책임과 행동에 대해 깊이 생각하고 고민한 결과를 표현할 수 있어야 한다. 역할, 책임, 협력에 집중하고 고민한 결과로 테스트-주도 개발을 활용할 수 있다.
05 책임과 메시지
객체가 명확하게 정의된 역할과 책임을 가지고 자율적으로 협력하는 것은 훌륭한 객체지향 설계에 필요한 요소이다. 객체는 자율적인 존재이기 위해 적절한 책임을 부여받아야 한다. 객체의 책임은 자신의 의지에 따라 수행할 수 있어야 하기 때문에 지나치게 세부적인 요구가 포함된 책임은 부적절한 한편 지나치게 포괄적이고 추상적인 책임은 협력의 의도를 명확하게 표현하지 못하기 때문에 부적절하다. 자율적인 책임의 특징은 어떻게가 아니라 무엇을 해야 하는가를 설명하는 것이다. 예를 들어, 모자 장수는 증언하라는 책임을 수행하도록 요청받으면서 증언하는 구체적인 방법은 자율적으로 선택할 수 있다.
객체는 메시지의 형태로 외부에서 전달되는 요청에 의해 행동을 시작한다. 수신자 객체는 메시지 인자를 통해 전달받은 추가 정보를 사용할 수 있다. 메시지를 수신한 객체는 해당 메시지를 처리할 수 있는지를 확인하고 자율적인 방법으로 책임을 수행한다. 메시지는 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다. 객체가 메시지를 처리하기 위해 내부적으로 선택하는 방법을 메서드라고 한다. 객체는 메시지를 수신하면 처리 가능 여부를 확인하고 메서드를 선택하여 실행한다. 실행 시간에 메서드를 실행하는 것은 객체지향 프로그래밍과 다른 프로그래밍 언어가 구분되는 특징이다.
다형성이란 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응하는 것을 의미한다. 다형성은 역할, 책임, 협력과 깊은 연관이 있다. 다형성은 메시지 송신자가 동일한 역할의 다양한 타입의 객체들과 협력할 수 있게 한다. 그래서 다형성은 동일한 역할을 수행할 수 있는 객체들 사이의 대체 가능성을 의미한다. 다형성은 설계를 유연하고 재사용 가능하게 한다. 수신자 객체와 송신자 객체가 메시지라는 얇은 끈으로만 이어져 있다면 객체 사이의 이 낮은 결합도가 설계를 유연하고 확장 가능하며 재사용 가능하게 한다.
책임-주도 설계의 핵심은 어떤 행위가 필요한지 탐색하고 이 행위를 수행할 객체를 결정하는 것이다. 이 과정을 What/Who 사이클이라고 한다. 협력이라는 문맥 안에서 필요한 메시지를 결정한 후에 메시지를 수신하기에 적합한 객체를 선택하는 것이다. 수신 가능한 메시지가 모여 객체의 인터페이스를 구성한다. 메시지를 결정하는 시점에는 어떤 객체가 메시지를 수신할 것인지 알 수 없기 때문에 메시지 송신자는 메시지 수신자 객체의 내부를 볼 수 없다. 이와 같은 '묻지 말고 시켜라' 스타일은 객체들의 자율성을 강조하고 캡슐화를 보장하며 결합도를 낮춘 유연한 설계를 가능하게 한다. 객체는 자신이 수신할 메시지를 결정하지 않아야 하고 협력에 필요한 객체를 발견해야 한다. 메시지 송신자는 메시지 수신자의 종류는 중요하지 않다. 메시지를 이해할 수만 있는 객체라면 다양한 타입으로 대체 가능하다. 메시지를 믿으면 자율적인 책임과 유연하고 확장 가능하며 재사용 가능한 설계가 따라올 것이다.
인터페이스는 다음의 세가지 특징을 가진다. 첫째, 내부 구조나 동작 방식을 몰라도 사용할 수 있어야 한다. 둘째, 내부 구조나 동작 방식을 변경하는 것이 인터페이스 사용자에게 영향을 미치지 않아야 한다. 셋째, 적용 대상이 변경되어도 동일한 인터페이스를 제공하면 문제 없이 상호작용 할 수 있어야 한다. 예를 들어, 운전자는 자동차의 내부 구조나 동작 방식을 모두 알지 못해도 자동차를 운전할 수 있어야 한다. 자동차의 내부 구조나 동작 방식이 변경되어도 운전자에게 영향을 미치면 안된다. 자동차 하나를 운전할 수 있으면 다른 종류의 자동차도 운전할 수 있어야 한다.
협력에 참여하는 객체들은 수신하는 메시지에 따라 인터페이스를 갖는다. 인터페이스는 외부에 공개된 공용 인터페이스와 내부에서만 접근하는 사적인 인터페이스로 구분된다. 객체가 협력에 참여하기 위해 송수신하는 메시지는 공용 인터페이스의 모양을 암시한다. 객체의 공용 인터페이스를 구성하는 것은 객체가 외부로부터 수신할 수 있는 메시지의 목록이다.
객체지향 사고 방식을 이해하기 위해 중요한 세가지 원칙은 다음과 같다. 좀 더 추상적인 인터페이스, 최소 인터페이스, 인터페이스와 구현 간의 차이를 인식하는 것이다. 객체의 자율성을 저해하는 과도하게 구체적인 책임은 지양해야 한다. 그리고 외부에서 사용할 필요가 없는 인터페이스는 최대한 노출하지 말아야 한다. 객체의 외부와 내부는 명확하게 분리헤야 한다. 객체지향 세계에서 내부 구조와 동작 방식을 가리키는 고유한 용어는 구현이다. 객체의 상태 그리고 객체의 행동인 메서드를 표현하는 것은 객체의 구현 부분이다.
인터페이스와 구현의 분리 원칙이 중요한 이유는 소프트웨어가 항상 변경되기 때문이다. 객체가 가지는 상태와 메서드 구현이 변경되어도 외부에 영향을 미쳐서는 안된다. 객체 외부에 영향을 미치는 변경은 공용 인터페이스를 수정하는 경우 뿐이다. 객체의 자율성을 보존하기 위해 구현을 외부로부터 감추는 것을 캡슐화라고 한다. 객체지향은 내부와 외부를 명확하게 구분하는 객체들로 구성된 협력 공동체이다.
객체의 협력이 자율적일수록 이해하기 쉬워지고 유연하게 변경할 수 있기 때문에 협력의 설계 품질을 결정하게 된다. 자율적인 책임은 협력을 단순하게 만들고 객체의 외부와 내부를 명확하게 구분하며 내부적인 방법을 변경해도 외부에 영향을 미치지 않게 한다. 그리고 자율적인 책임은 협력의 대상을 다양하게 선택하는 유연성을 제공한다. 객체가 수행하는 책임들이 자율적일수록 객체의 역할을 이해하기 쉽다. 다시 말해서, 책임이 자율적일수록 객체가 적절하게 추상화되고 응집도는 높아지고 결합도는 낮아지며 캡슐화가 증진되고 인터페이스와 구현이 명확히 분리되며 설계의 유연성과 재사용성은 향상된다.
06 객체 지도
소프트웨어 제품 설계는 기능 측면의 설계와 구조 측면의 설계로 구성되며 안정적인 객체 구조를 바탕으로 설계해야 한다. 예를 들어, 길을 찾는다는 목표를 달성하기 위해 행인에게 물어보는 것이 기능적 측면의 접근 방법이라면 지도를 참고하는 것은 구조적 측면의 접근 방법이다. 좋은 기능이 좋은 소프트웨어의 충분조건이라면 좋은 구조는 좋은 소프트웨어의 필요조건이다. 변경되는 요구사항에 대응하려면 자주 변경되는 기능보다는 안정적인 구조를 중심으로 설계해야 한다. 객체를 기반으로 책임과 역할을 식별하고 메시지를 기반으로 객체간의 협력 관계를 구축하는 이유는 변경되지 않는 안정적인 객체 구조를 바탕으로 시스템 기능을 객체 간 책임으로 분배하기 위함이다.
도메인 모델링은 구조를 수집하고 표현하기 위한 기법으로서 변경에 유연한 좋은 객체지향 설계를 위해 사용된다. 도메인은 사용자가 프로그램을 사용하는 대상 분야를 의미한다. 도메인 모델은 사용자가 대상을 단순화하고 의식적으로 구조화한 것이다. 도메인 모델은 단순히 다이어그램이 아니라 이해관계자들이 자신이 상호작용하는 사물들에 대해 갖는 개념을 추상화한 멘탈 모델이다. 객체지향을 사용하면 도메인에 대한 사용자 모델, 디자인 모델, 시스템 이미지가 모두 유사한 모습을 유지하도록 만들 수 있다. 이것을 연결완전성 또는 표현적 차이라고 표현한다. 소프트웨어 객체는 그 대상이 현실적인지 않은지에 상관없이 도메인 모델을 통해 표현되는 도메인 객체들을 은유해야 한다. 도메인 모델은 코드 속의 미로를 헤쳐나갈 지도를 제공한다. 도메인 모델은 안정적인 구조를 바탕으로 변경에 유연하게 대응하는 소프트웨어를 설계할 수 있게 한다.
유스케이스는 사용자와 시스템 간의 상호작용 흐름을 정리한 것이다. 유스케이스를 사용하면 사용자들의 목표를 중심으로 시스템의 기능적 요구사항들을 묶을 수 있다. 유스케이스는 사용자와 시스템 간의 상호작용을 보여주는 텍스트이며 여러 시나리오들의 집합이다. 유스케이스는 단순한 피처 목록으로 시스템의 기능을 나열한 목록이 아니다. 그리고 유스케이스는 사용자 인테페이스와 관련한 세부 정보를 포함해서는 안된다. 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다. 유스케이스는 객체지향과도 상관이 없다. 유스케이스는 기능 요구사항을 사용자의 목표라는 문맥을 중심으로 묶기 위한 정리 기법이다.
책임-주도 설계 방법은 시스템의 기능과 역할을 책임을 수행하는 객체들의 협력 관계로 보게 함으로써 도메인 모델과 유스케이스를 통합한다. 불안정한 기능을 안정적인 구조 안에 담아 변경에 대한 파급효과를 최소화하는 것이 좋은 객체지향 설계이다. 시스템에 할당된 커다란 책임을 더 작은 규모의 객체들이 수행하도록 더 작은 규모의 책임으로 세분화하기 위해 도메인 모델을 사용할 수 있다. 객체의 이름과 책임을 도메인 모델에 정의한 개념에 적합하게 할당한다. 그리고 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공한다.
07 함께 모으기
마틴 파울러는 객체지향 설계에 대하여 다음의 세가지 관점을 설명한다. 첫째, 개념 관점에서 설계는 도메인 안에 존재하는 개념들 사이의 관계를 표현한다. 둘째, 명세 관점에서는 사용자의 영역인 도메인으로부터 개발자의 영역인 소프트웨어로 초첨이 옮겨진다. 셋째, 구현 관점에서는 객체들이 책임을 수행하는 데 필요한 코드로 초점이 옮겨진다. 객체가 협력 안에서 메시지를 선택하고 메시지를 수신할 객체를 선택하는 것은 객체의 인터페이스, 즉 명세 관점에서 객체를 보는 것이다. 이번 장은 명세 관점에 더해 개념 관점과 구현 관점을 다룰 것이다.
커피 주문을 예로 들어보자. 객체지향 관점에서 커피 전문점이라는 도메인은 손님, 메뉴, 메뉴 항목, 바리스타, 커피라는 객체들로 구성된다. 이 객체들은 커피 전문점을 구현하는 중요한 개념과 관계를 반영한다. 클래스들을 보는 것은 개념 관점에서 코드를 본 것이다. 하나의 메뉴판 객체는 다수의 메뉴 항목 객체로 구성되는데 이것을 포함 또는 합성 관계로 나타낼 수 있다. 손님과 메뉴판의 관계와 같이 포함하지는 않지만 서로 알고 있어야 하는 관계는 연관 관계라고 한다.
객체지향 설계에서 협력을 설계할 때는 객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다. 커피를 주문하라는 메시지를 수신할 객체는 손님이고 커피 주문이라는 책임을 할당 받는다. 손님 객체의 인터페이스 안에는 커피를 주문하라는 오퍼레이션이 포함되어야 한다. 클래스의 인터페이스를 바라보는 것은 명세 관점에서 코드를 본 것이다. 마지막으로 오퍼레이션을 수행하는 방법을 메서드로 구현해야 한다. 클래스의 내부 구현을 보는 것은 구현 관점에서 코드를 본 것이다.
객체지향의 사실과 오해를 읽고나서
이상으로 전적으로 주관적으로 각 장마다 단락마다 핵심 내용이라고 생각되는 문장들을 뽑아낸 글 무더기들의 모음이 완성되었다. 두괄식도 미괄식도 아닌 단락들이 마구잡이로 섞여있는 글 같지만 나름 고민해서 추린 문장들이다. 모든 글이 이해가 되지는 않지만 핵심 내용이 마음에 들고 기대가 된다면 책을 사서 읽어보기를 바란다. 필자는 다음과 같은 이유로 이 책을 재미있게 읽을 수 있었다.
객체지향 프로그래밍이 갖는 특징과 강점을 이해하는 것이 앞으로 만나게 될 프로젝트들을 분석하는 안목을 키워줄 뿐만 아니라 더 나아가 좋은 객체지향 설계를 할 수 있는 발판을 마련하리라는 믿음으로 책을 읽어내려갔다. 이제부터는 프로젝트의 코드를 분석할 때에 클래스들을 협력에 참여하는 살아있는 자율적인 객체들로 볼 수 있을 것이다. 인터페이스를 객체의 자율성을 높이고 변화에 유연하도록 하는 장치로서 볼 수 있을 것이다. 메서드는 객체가 책임을 수행하기 위해 자율적으로 선택하는 방법들로 볼 수 있을 것이다. 더 나아가 객체지향 설계와 구현을 할 때에 객체가 수신할 메시지를 찾고 적절한 객체에게 책임을 할당할 수 있을 것이다. 메시지를 수신하기 위한 인터페이스를 명세하고 내부 메서드를 구현할 수 있을 것이다. 협력하는 객체들의 공동체로서 객체지향 세계를 창조하는 프로그래밍을 할 수 있을 것이다.
객체지향 프로그래밍 방식으로 구현하는 소프트웨어를 현실 세계와 구분되는 또다른 세계라고 표현하면서 프로그래밍을 창조의 과정으로 볼 수 있게 하는 관점도 책을 몰입도 있게 읽어 내려감에 있어서 도움을 준 것 같다. 객체지향 언어에서 클래스가 단순히 C언어의 구조체나 일부 자료구조들 같이 데이터를 담는 틀의 역할만을 하는 것이 아니라는 관점은 다른 프로그래밍 방식보다 객체지향 프로그래밍 방식이 더 창조적이고 흥미로운 방식이라는 생각이 들게 한다. 객체지향 세계가 현실 세계의 모방이 아니라 오히려 새로운 세계를 창조하는 것이라는 관점으로 프로그래밍을 하는 것이 오히려 설계와 구현 과정에서 어떤 사고방식의 제약도 갖지 않고 프로그램의 진짜 의도와 비즈니스적인 목표에 집중할 수 있게 해줄 것 같다.
객체지향의 사실과 오해를 통해 얻게 된 객체지향 프로그래밍에 대해 더 확장되고 명확해진 이해와 관점들을 실무에서 잘 활용할 수 있었으면 좋겠다. 단순히 객체지향 프로그래밍 방식으로 작성된 소프트웨어를 분석하는 것 뿐만 아니라 직접 설계하고 구현하는 과정을 거치며 더 깊이 있게 이해하고 적용할 수 있었으면 좋겠다. 이 책의 저자처럼 새로운 세계를 창조하는 과정으로 객체지향 프로그래밍 방식을 사용해 소프트웨어를 설계하고 구현하는 경험을 하기를 기대한다.
참고문헌
조영호. (2015). 객체지향의 사실과 오해. 위키북스.