▼ What ?
이번 4장에선 제목에서도 예상해볼 수 있듯이 1장부터 3장까지 언급한 개념들을 취합해서 객체지향을 어떻게 설계해야 하는지를 설명해주는 내용을 담고 있다. 그렇기 때문에 '역할', '책임', '협력' 이라는 개념 외에도 '책임-주도 설계', '디자인 패턴' 그리고 '테스트-주도 개발' 에 대해서도 다루고 있고, 이번 4장이 지금까지 이 책을 통해 배운 내용들을 정리해볼 수 있는 중요한 챕터인 것 같다.
▼ Summory & Comment
이 챕터를 읽고 나서 기억에 남는 말 !
객체지향 시스템에서 가장 중요한 것은 충분히 자율적인 동시에 충분히 협력적인 객체를 창조하는 것이다. 이 목표를 달성할 수 있는 가장 쉬운 방법은 객체를 충분히 협력적으로 만둔 후에 협력이라는 문맥 안에서 객체를 충분히 자율적으로 만드는 것이다.
- 이 말을 이해하기 위해선 객체지향을 설계하는 흐름을 이해해야 하고, 객체와 협력 사이에 어떤 관련성을 갖고 있는지를 알아야 한다고 생각이 들었다. 그리고 이 책에서 계속해서 우리에게 해주려는 말이 "충분히 자율적인 동시에 충분히 협력적인 객체를 창조" 하라는 것이었는데, 이걸 어떻게 해야 하는지를 간단명료하게 설명해주는 말인 것 같아서 기억에 남았던 것 같다.
협력
- 인간은 완벽하게 이기적인 동시에 합리적인 존재 ?
➜ 독일 경제학 게임 "최후통첩 게임(Ultimaum Game)"
➜ 인간은 이기적이고 합리적인 본연의 '특성' 에 따라 자신의 이익을 최대화되도록 행동하지 않는다 !
➜ 인간의 행동을 결정하는 것은 '협력' 이다 ! - '협력' 은 한 사람이 다름 사람에게 요청함으로써 요청 받은 사람도 요청한 사람에게 응답하는 과정을 의미한다.
➜ 협력 = 다수의 연쇄적인 요청 + 응답 - "이상한 나라의 앨리스" 이야기에서 하트 잭에 대한 공판이 열리고 있는 법정의 모습에 객체지향 패러다임을 적용시켜보자.
➜ 객체지향의 세계는 동일한 목적(하트 잭의 재판)을 달성하기 위해 협력(요청 + 응답)하는 '객체'
- 재판 장면에 등장하는 누군가 ➜ 재판 요청 ➜ 왕(재판 요청)
- 왕 ➜ (증인 호출 요청) ➜ 토끼 / 토끼 ➜ (증인 호출 응답) ➜ 왕
- 토끼 ➜ (증인 소환 요청) ➜ 모자 장수 / 모자 장수 ➜ (입장 응답) ➜ 토끼
- 왕 ➜ (증언 요청) ➜ 모자 장수 / 모자 장수 ➜ (증언 응답) ➜ 왕
- 재판 장면에 등장하는 누군가 ➜ 재판 요청 ➜ 왕(재판 요청)
- '요청' 과 '응답' 은 협력(재판)에 참여하는 객체(왕, 토끼, 모자 장수)가 수행할 책임이다 !
- '요청' 을 받기 위해선 해당 객체가 그 요청에 대해 응답할 수 있는 지식과 행동방식을 갖고 있어야 한다.
책임
- 객체의 책임은 '하는 것(Doing)' 과 '아는 것(Knowing)' 으로 분류될 수 있고, 이 두 가지가 객체지향 설계의 품질을 결정하는 중요한 요소이다 !
( 외부에서 접근 가능한 공용 서비스의 관점 )
➜ 객체의 외부에 제공해 줄 수 있는 정보(Knowing) / 서비스(Doing)
➜ 따라서, '책임' 은 객체의 '공용 인터페이스(public interface)' 를 구성하고, 이는 '캡슐화(Encapsulation)' 로 이어진다.
- 무엇을 알고 있는가 (Knowing)
- 개인적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
➜ "하얀 토끼가 모자 장수를 안다." - 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
➜ "모자 장수가 자신이 증언할 수 있다는 것을 안다."
- 무엇을 할 수 있는가(doing)
- 객체를 생성하거나 계산하는 것과 같이 스스로 하는 것
➜ "모자 장수가 증인석에 입장한다." - 다른 객체의 행동을 시작시키는 것
➜ "하얀 토끼가 모자 장수에게 증인석에 입장할 것을 요구한다." - 다른 객체의 활동을 제어하고 조절하는 것
- 객체를 생성하거나 계산하는 것과 같이 스스로 하는 것
- 무엇을 알고 있는가 (Knowing)
- '책임' 의 관점과 '메시지' 관점에서 바라보는 협력 ?
- 책임 ➜ 해당 객체가 '무엇' 을 수행해야 하는지를 나열하는 것에 중점을 둔다.
- 메시지 ➜ 협력에 참여하고 있는 두 객체 사이의 '관계' 에 중점을 둔다.
- '메시지를 수신한다' 는 것은 '책임을 수행해야 한다' 는 것을 의미한다.
➜ 즉, '메시지' 는 곧 '책임' 을 의미 ! - 하지만, '책임' 과 '메시지' 가 같은 레벨에 있다고 착각 X
➜ 책임(상위 레벨)을 결정한 후 실제로 협력을 정제하면서 메시지로 변환할 때 하나의 책임이 여러 메시지로 분할되는 것이 일반적이다. - 협력에 참여하기 위해 어떤 객체가 어떤 책임을 수행해야 하고 어떤 객체로부터 메시지를 수신할 것인지를 결정함으로써 개요를 아는 것만으로도 객체지향 설계를 시작하는데 충분하다 !
➜ 어떤 '클래스' 가 필요하고 어떤 '메서드' 를 포함해야 하는지는 이 이후에 결정한다.
역할
- '재판' 이라는 협력에 참여하기 위해 왕은 '판사' 라는 역할을, 모자 장수는 '증인' 이라는 역할을 수행
- 왕은 왕이라고, 모자 장수는 모자장수라고 하면 될텐데 굳이 '역할' 을 지정해주는 이유는 ?
➜ 왕이 여왕으로, 모자 장수가 앨리스로 '대체' 되어도 재판이라는 협력 과정엔 변함이 없도록 !
➜ 즉, '역할' 은 객체지향 설계에서 단순성(simplicity), 유연성(flexibility), 재사용성(reusability)을 뒷받침하는 핵심 개념이다. - 아래처럼 "왕 - 토끼 - 모자 장수" 의 협력, "여왕 - 토끼 - 요리사" 의 협력, "여왕 - 토끼 - 앨리스" 의 협력이 있다고 하자.
➜ 이러한 세 가지 협력을 하나의 협력으로 '추상화' 하여 단순화할 수 있게 된다.
➜ 이처럼 역할(role)이 협력을 추상적으로 만들 수 있는 것은, 역할 자체가 객체를 '추상화' 한 것이기 때문이다 !
➜ 그리고 이는 역할의 '대체 가능성' 에 비롯되는 것 !
- 객체가 역할을 대체하기 위해선 위 그림에서도 알 수 있듯이 행동이 호환되어야 한다는 조건이 필요하다.
➜ 여왕(객체)이 왕(객체)처럼 "재판하라" 라는 요청 메시지를 이해하고 "목격자를 불러오라", "증언하라" 와 같은 동일한 행동(다른 객체에게 요청 메시지 전달)을 하여 판사의 역할을 수행할 수 있을 때, 왕이 여왕으로 대체될 수 있는 것이다.
➜ 이미 첫 번째 장 "01_협력하는 객체들의 공동체" 에서 '역할' 은 책임의 집합이며 여러 객체가 동일한 역할을 수행 가능, 즉 "역할은 대체 가능하다." 라는 개념을 언급했었다.
➜ '동일한 역할' 을 수행하는 객체들은 '동일한 메시지' 를 수신할 수 있기 때문에 '동일한 책임의 집합' 을 수행할 수 있다는 것은 중요한 개념 ! - 여기서 조심해야 할 점은 '판사' 역할이 여왕에게 주어졌다고 여왕이 '판사' 역할에 해당하는 책임만 수행하는 것이 아닌 것처럼, 객체가 역할에 주어진 책임 이외에 다른 책임도 수행할 수 있다는 사실을 알아야 한다 !
- 세 번째 장인 "03_타입과 추상화" 에서 다뤘던 주요 개념인 '타입' 도 객체를 추상화한 '개념' 이었다.
➜ 그렇다면, '역할과 타입 모두 역할과 타입 사이엔 어떤 관계가 있을까?' 라는 생각이 들었는데, '일반화/특수화 관계' 에 있다고 할 수 있다고 한다.
➜ '역할' 은 좀 더 일반적이고 포괄적인 개념이고, '타입' 은 좀 더 구체적인 개념인 것 !
( 이 속에서도 '일반적인 타입' > '특수한 타입' 이 있는 것 ) - 역할의 대체 가능성 ➜ 행위 호환성 ➜ 동일한 책임 수행
객체의 모양을 결정하는 협력
- 객체지향에서의 중요한 개념은 ?
- 시스템에 필요한 데이터가 아닌 객체의 행동, 즉 '책임' 이다.
➜ '객체' 는 데이터를 저장하기 위해 존재하는 것이 X
➜ '데이터' 는 그냥 행동(책임)을 수행하기 위해 필요한 재료(상태), 그리고 '행동' 은 협력에 참여하기 위한 수단이다 ! - 정적인 클래스가 아닌 협력에 참여하는 동적인 객체에 중점을 두자 !
➜ '클래스' 는 단지 객체를 구현하기 위한 메커니즘.
- 시스템에 필요한 데이터가 아닌 객체의 행동, 즉 '책임' 이다.
- 데이터나 클래스를 중심으로 객체지향 설계를 시작하게 되는 이유는 '협력' 이라는 중요한 개념을 고려하지 않고 객체를 독립적으로 생각하기 때문이다.
➜ "이상한 나라의 앨리스" 라는 애플리케이션을 개발하는 상황을 예로 들었을 때, 정적인 클래스를 기반으로 '왕' 의 인스턴스를 모델링한다면 전형적인 '왕' 의 모습을 떠올리고 그러한 모습을 클래스로 구현하게 되는 문제가 발생한다.
➜ 그게 아니라 '왕' 이 참여하는 '재판', 즉 '협력' 을 우선적으로 고려하고 '왕' 이 아닌 재판에 참여하는 '판사' 의 역할에 필요한 책임을 생각하고 '왕' 이라는 객체를 클래스로 구현해내야 하는 것이다.
➜ 올바른 '객체' 를 설계하기 위해선 견고하고 깔끔한 '협력' 을 먼저 설계해내야 한다.
( 그래서 객체의 모양을 결정하는 것은 협력이라 하는 것이다. )
➜ 그리고, 협력을 설계한다는 것은 협력에 참여하는 객체들이 주고받을 '요청' 과 '응답' 의 흐름을 결정하는 것을 의미한다.
- 객체지향을 설계하는 과정
- 목표를 달성하기 위해 필요한 '협력', 그러한 협력을 설계하기 위해 필요한 '책임' 을 고안한다.
( "이상한 나라의 앨리스" 이야기라는 목표를 달성하기 위해 필요한 협력은 '재판' 이고, 이 '재판' 을 설계하기 위해서 필요한 일련의 '책임' 엔 '재판 진행', '증인 소환', '증언' 이 있다. ) - 객체들에게 앞서 고안한 '책임'(역할)을 할당한다.
( 어떤 책임은 왕에게, 어떤 책임은 하얀 토끼에게, 어떤 책임은 모자 장수에게 ? ) - 책임은 객체가 외부에 제공하게 될 '행동' 을 정의한다.
( 왕이 해야 할 행동, 토끼가 해야 할 행동, 모자 장수가 해야 할 행동은 ? ) - 행동을 결정하고 나면, 행동에 필요한 재료인 '데이터' 를 고려한다.
( 왕, 토끼, 그리고 모자 장수가 할 행동들에 필요한 특성은 뭐지 ? ) - 이렇게 객체가 협력을 참여하기 위해 필요한 행동과 데이터가 결정된 후에 클래스의 구현 방법을 결정한다.
( 왕, 토끼 그리고 모자 장수를 구현하는 클래스를 개발하자 ! )
- 목표를 달성하기 위해 필요한 '협력', 그러한 협력을 설계하기 위해 필요한 '책임' 을 고안한다.
- '객체지향' = 올바른 객체에 올바른 책임을 할당하는 것.
- '객체지향 애플리케이션을 구현' = '협력' 이라는 문맥 안에서 객체를 생각하는 것.
- 객체지향 시스템에서 가장 중요한 것은 충분히 자율적인 동시에 충분히 협력적인 객체를 창조하는 것이고, 이러한 객체를 생성하는 가장 쉬운 방법이 바로 (1)충분히 견고하고 깔끔한 '협력'을 설계하고 그러한 (2)'협력' 이라는 문맥 속에서 객체를 충분히 자율적으로 만드는 것이다 !
객체지향 설계에 유용한 기법
책임-주도 설계(Responsibility-Deriven Design)
- 협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당하여 애플리케이션을 설계하는 방식을 말한다.
➜ 객체지향 시스템은 역할과 책임을 수행하는 자율적인 객체들의 공동체이고, 객체지향 시스템의 목적은 사용자의 요구를 만족시킬 수 있는 기능을 구현하기 위한 '협력 관계' 를 고안하고, 협력에 필요한 '역할' 과 '책임' 을 식별한 후 이에 적합한 객체 식별하여 '할당해 나가기 위함이다 !
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.
- 시스템 책임을 더 작은 책임으로 분할한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 중에 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 된다.
➜ 즉, 역할, 책임, 협력에 집중하며 '객체지향' 을 협력하는 객체들의 공동체로 바라보는 관점은 유연하고 견고한 객체지향 시스템을 설계하는 데 필요한 강력한 프레임워크를 제공해준다.
디자인 패턴(Design Pattern)
- 전문가들이 반복적으로 발생하는 특정 문제를 해결하기 위해 이미 식별해 놓은 역할, 책임, 협력의 템플릿(모음)이기 때문에, 이미 있는 패턴은 다시 만들 필요가 없다.
➜ "바퀴를 다시 발명하지 마라." (Don't reinvent the wheel.)
( 23개의 디자인 패턴을 정리해둔 책인 "GOF의 디자인패턴" 이 유명하다 ) - '디자인 패턴은' 반복적으로 발생하는 문제에 적용할 수 있는 '책임-주도 설계' 의 결과물이다.
➜ 특정한 상황에 적용 가능한 디자인 패턴만 잘 알고 있다면, '디자인 패턴' 은 앞서 말한 책임-주도 설계 절차를 순차적으로 따르지 않더라도 시스템 안에 구현할 객체들의 역할과 책임, 협력 관계를 빠르고 손쉽게 포착할 수 있는 지름길이 되어준다.
Composite(복합체) 패턴
- 윈도우 파일 탐색기를 예로 들면, 우리는 대상이 파일인지 폴더인지 신경쓰지 않고 그냥 대상을 선택하고 경로를 바꾼다는 사실을 떠올려볼 수 있다.
➜ 즉, 클라이언트 입장(사용자)에서 메시진 수진자가 개별 객체(파일)인지 복합 객체(폴터)인지 상관 없이 동일한 메시지(경로 변경)를 이용해 동일한 방식으로 대상과 상호작용할 수 있도록 해주는 패턴이 'Composite 패턴' 이다.
- Client : Component에게 메시지를 요청함으로써 협력하는 임의의 '역할'
- Component : Client와 협력할 수 있는 공용 인터페이스를 정의하는 '역할'
- Leaf : 공용 인터페이스에 대한 operation 호출에 응답할 수 있는 기본적인 행위를 구현하는 '역할'
- Composite : 외부로부터 Component의 구현체(다른 Component 추가(add())/삭제(remove()), 포함된 컴포넌트를 반환(getChild()))를 감추고 Composite에 포함된 부분을 하나의 단위로 행동할 수 있게 해주는 '역할'
➜ 위 그림에서 중요하게 봐야 할 점은 은 위의 구조를 구성하고 있는 요소들이 클래스와 메서드가 아니라 Client, Component, Leaf, Composite과 같이 '협력' 에 참여하는 '역할' 과 '책임' 이라는 것 !
- 구조가 프록시(Proxy) 패턴과 유사한 것 같아서 어떤 점에서 차이가 있는지 찾아봤다.
- 프록시 패턴은 객체에 대한 접근을 제어하는 데 중점을 둔다.
- 복합체 패턴은 객체를 트리 구조로 구성하여 단일 및 복합 객체를 동일하게 취급하는 것을 목적으로 한다.
- 프록시 패턴은 객체에 대한 접근을 제어하는 데 중점을 둔다.
프록시 패턴 (참고 자료) - [Software Design Pattern] 프록시 패턴 (Proxy Pattern) — Uykm_Note (tistory.com)
[Software Design Pattern] 프록시 패턴 (Proxy Pattern)
▼ Why ? What ? GDSC - Web 커리큘럼에서 빈(Bean)을 등록하는 과정에서 빈(Bean)을 수동으로 등록하는 것과 자동으로 등록하는 것의 차이를 잘 모르는 것 같아 다시 공부를 했다. 빈을 수동으로 등록할
ukym-tistory.tistory.com
테스트-주도 개발(Test-Driven Development)
- 테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하면서 애플리케이션을 완성해가는 방식이다.
( + 리펙토링을 통해 중복 제거 ) - 일반적으로 우리가 알고 있는 테스트를 작성한다고 생각하면 '테스트-주도 개발' 이 어떤 식으로 작동하는지 이해할 수 없다.
➜ 책임을 수행할 개체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 '기대' 를 코드의 형태로 작성하는 것이라 생각하자 !
➜ 즉, 설계한 역할, 책임, 협력이 적합한지를 테스트를 통해 피드백을 하는 것이다. - 테스트-주도 개발은 결국 '책임-주도 설계' 를 통해 달성해야 할 목표에 좀 더 빠르고 견고한 방법으로 도달할 수 있도록 '테스트' 라는 안전장치를 설치해두는 것이다.
- 테스트-주도 개발을 객체지향 설계의 관점으로 생각해보자.
- 테스트를 작성하기 위해 객체의 메서드를 호출하고 반환값을 검증하는 것은 ?
➜ 객체가 수행해야 할 '책임' 을 생각하는 것이다. - 테스트에 필요한 간접 입력 값을 제공하기 위해 '스텁(stub)' 을 추가하거나 간접 출력 값을 검증하기 위해 '목 객체(mock object)' 를 사용하는 것은 ?
➜ 객체화 협력해야 하는 '협력자' 에 대해 고민한 결과를 코드로 표현한 것이다.
- 테스트를 작성하기 위해 객체의 메서드를 호출하고 반환값을 검증하는 것은 ?
- '테스트-주도 개발' 은 일종의 보험과 같다고 생각이 들었다.
➜ '테스트-주도 개발' 없이도 원칙과 절차만 잘 지킨다면 훌륭한 객체지향 설계를 해내는 것은 가능한다.
➜ '책임-주도 설계' 의 기본 개념이라든가 다양한 원칙, 패턴들을 종합적으로 이해하고 좋은 설계에 대한 감각과 경험이 있다면, 즉 역할, 책임, 협력에 집중하며 객체지향의 원칙을 적용하려는 깊이 있는 고민과 노력 같은 보험료만 지불해낸다면 '테스트-주도 개발'은 결과에 대한 안정감을 보장해주는 보험의 혜택과 같은 것이다.
▼ Study
"객체를 섬으로 바라보는 잘못된 눈길을 거두고 올바른 곳을 바라보는 것이 중요하다."
- 이 말이 무엇을 의미하는걸까?
➜ '고립된' 섬처럼 한 객체만을 생각하지 말고 협력이라는 문맥 속에서 바라보자 !
Issue_1
"책임은 객체의 외부에 제공해 줄 수 있는 정보(아는 것의 측면)와 외부에 제공해 줄 수 있는 서비스(하는 것의 측면)의 목록이다."
여기서 '아는 것(knowing)' 에 해당하는 외부에 제공해 줄 수 있는 정보가 책임에서 어떤 부분에 해당하는 것인지 감이 잘 안잡힙니다. 일단 '책임' 자체가 객체가 해야 할 행동을 의미하니까 '아는 것' 이 의미하는 것은 객체에 저장되는 데이터(상태)는 아니라는 건데.. 객체지향 설계를 하면서 '책임' 을 고안할 때 '아는 것' 을 어디 부분까지 포함해서 고려하는 것이 좋을지 모르겠습니다.
- 책임을 '아는 것' 과 '하는 것' 으로 분류했을 때 ' 아는 것' 은 행위에 필요한 재료, 즉 데이터 자체가 아니라 필요한 데이터를 알고 있을 것이라는 '전제' 에 해당되는 것이라 생각합니다. "이상한 나라의 앨리스" 에서 모자 장수를 예를 들면, 모자 장수에게 책임을 부여할 때 모자 장수는 '자신이 증언할 수 있다는 것을 안다' 는 정도일 뿐이지, 무엇을 증언할 것인지까지는 책임이 아닌 데이터(속성)에 해당하는 부분인 것입니다. 따라서, 객체지향 설계에서도 객체에 책임을 부여할 때 해당 객체가 무엇을 알고 있어야 하는 정도까지만 고려를 하고 설계를 하는 것이지, 실질적으로 알고 있는 자세한 내용(데이터)은 협력, 책임 그리고 행동을 모두 고안해낸 이후에 생각해볼 부분인 것 같습니다.
Issue_2
"책임은 응집도 있는 행위의 집합으로 행위의 집합이다. 객체가 알아야 하는 정보와 객체가 수행할 수 있는 행위에 대해 개략적으로 서술한 문장이다."
여기서 책임을 객체 자체에 대입할 수 있을까요? 아니면 책임에 필요한 객체 안의 행동들에게만 적용될 수 있을까요?
제 생각은 위에서 행위의 집합이라고는 하지만 '아는 것'에서 '개인적인 정보에 관해 아는 것'은 객체 안의 데이터도 포함된다고 생각해서 행위 안에 데이터 또한 포함될 수 있으니 책임에 필요한 구성요소(데이터, 행위)에 해당된다고 봅니다!
- Issue_1의 답변으로 해결될 것 같습니다.
Issue_3
"한 가지 주의할 점은 책임과 메시지의 수준이 같지는 않다는 점이다. 책임을 객체가 협력에 참여하기 위해 수행해야 하는 행위를 상위 수준에서 개략적으로 서술한 것이다. 책임을 결정한 후 실제로 협력을 정제하면서 이를 메시지로 변환할 때는 하나의 책임이 여러 메시지로 분할되는 것이 일반적이다."
책임과 메시지의 수준이 같지 않다는 말의 의미가 책임이 여러 메시지로 변환될 수 있다는 말이고, 결국 책임을 메시지의 상위의 개념으로 이해하면 될까요 ?
- 책임과 메시지가 의미하는 바는 같지만, 책임을 먼저 생각하고 어떤 메시지로 분할해야 할지를 고민하라는 말을 전하는 것 같습니다. (메시지는 책임의 하위 레벨 !)
Issue_4
"객체는 역할이 암시하는 책임보다 더 많은 책임을 질 수 있다. 따라서 대부분의 경우에 객체의 타입과 역할 사이에는 일반화/특수화 관계가 성립하는 것이 일반적이다."
객체의 타입과 역할 사이에는 일반화/특수화 관계가 성립한다는 말이 무슨 말인지 모르겠습니다!!!!
- '판사' 라는 역할이 '트럼프 왕' 이라는 타입보다 좀 더 포괄적인 개념이듯이, '역할' 은 좀 더 일반적이고 포괄적인 개념이고, '타입' 은 좀 더 구체적인 개념이라는 것을 말하는 것 같습니다.
( 이 '타입' 속에서도 '일반적인 타입' > '특수한 타입' 이 있는 것 ! )