🧐 책을 읽게 된 이유
그 유명한 토끼책을 추천 받아 읽게 되었다. '객체지향'이라는 단어와 언어(Swift)를 자연스럽게 사용하면서도 정작 그 의미에 대해 잘 알지 못했었는데, 멋있는 분이 추천해주셔서 마음 먹을 수 있게 되었다. 원래 구매해서 읽으려고 했는데 운이 좋게도 아카데미 기숙사 룸메이트가 마침 가지고 있어 빌려 읽을 수 있었다!
🐰 느낀점 적어보기
객체지향의 근본적인 의미를 이해하는 것이 왜 어려운지 알게 되었다. 객체지향을 설명할 때 '실제 세계의 모방'을 예시로 들곤 하는데, 이 때문에 내가 원래 가지고 있던 관념과 연결시키기 어려웠던 것 같기도 하다. (실제 세계를 모방하다보면 이해할 수 없는 것들이 많기 때문에) 이 책에서 객체지향은 '실제 세계의 은유'라는 표현을 사용한다. 그리고 새로운 세계를 창조하는 것이라 이야기 한다. 이 문장이 저자가 말하고자 하는 핵심이 아닐까 싶다.
개발적인 관점 외에서도 재밌는 부분이 많았다. '개념'이라던가, '추상화' 등 이 세상을 이해할 수 있게 돕는 다양한 예시들이 기억에 남는다. 결국 객체지향 패러다임이 주목받게 된 이유도 불완전한 우리들 때문일 것이다. (AI에게는 객체지향 프로그래밍이 정말 비효율적이고 이해할 수 없을수도 있지 않을까?)
결론적으로 이 책을 읽고 나의 하드스킬이 성장했다 보기에 어려움이 있을 수 있겠지만, 시야가 넓어진 것은 확실하다. (서문에서도 이게 목적이라고 했었는데 완전 성공이다) 왜 객체지향이라는 개념이 등장했고, 왜 객체지향 프로그래밍이 중요한지 이해할 수 있게 만들어준 고마운 책이다.
1장: 협력하는 객체들의 공동체
객체 지향의 첫걸음
- 클래스가 아닌 '객체'를 바라보는 것.
- 객체를 독립적인 존재가 아닌 기능 구현을 위해 협력하는 공동체의 존재로 바라보는 것.
- 협력에 참여하는 객체들에게 얼마나 적절한 역할과 책임을 부여할 수 있는가?
- 위 3가지 개념을 프로그래밍 언어에 담아낼 수 있는 기술을 익히는 것.
객체 지향의 목표는 실세계를 모방하는 것이 아니다. 오히려 새로운 세계를 창조하는 것이다.
역할, 책임, 협력
객체지향 설계는 적절한 객체에게 적절한 책임을 할당하는 것에서 시작된다."
- 역할은 관련성 높은 책임의 집합이다."
- 협력의 성공은 특정한 역할을 맡은 각 개인(객체)이 얼마나 요청을 성실히 이행하는가에 달렸다.
- 특정한 역할은 특정한 책임을 암시한다.
- 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력 -> 다형성(polymorphism)
객체가 갖춰야 할 2가지 덕목
- 객체는 충분히 협력적이어야 한다.
- 객체는 다른 객체의 요청에 귀기울이고
- 다른 객체에게 적극적으로 도움을 요청할 열린 마음을 지녀야 한다.
- 외부의 도움을 무시한 채 모든 것을 스스로 처리하려고하는 전지전능한 객체는 내부의 복잡도에 의해 자멸할 수 있다.
- 객체는 충분히 자율적이어야한다.
- 객체는 다른 객체의 명령에 복종하는 것이 아닌, 요청에 응답할 뿐이다.
- 어떤 방식으로 응답할지는 객체 스스로가 판단 후 결정한다.
- 요청에 응할지 여부도 스스로 결정 가능하다.
객체는 다른 객체가 무엇을(What) 수행하는지는 알 수 있지만, 어떻게(How) 수행하는 지에 대해서는 알 수 없다.
그래서 객체지향이 뭔데?
시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법
2장: 이상한 나라의 객체
세상을 더 작은 객체로 분해하는 것은 본질적으로 세상이 포함하고 있는 복잡성을 극복하기 위한 인간의 작은 몸부림이다.
상태, 행동, 식별자
- 상태를 이용하면 과거의 모든 행동 이력을 설명하지 않고도 행동의 결과를 예측 및 설명할 수 있다.
- 모든 객체의 상태는 단순한 값과 객체의 조합으로 표현할 수 있다.
- 객체는 상태를 캡슐 안에 감춰둔채 외부로 노출하지 않는다. 객체가 외부에 노출하는 것은 행동뿐이며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동 뿐이다.
객체 지향 초보가 가장 쉽게 빠지는 함정
❌ 상태를 먼저 결정하고 행동을 나중에 결정하는 것
- 상태가 아닌 행동에 초점을 맞춰야 한다.
- 어떤 책임이 필요한가를 결정하는 과정이 전체 설계를 주도해야 한다 -> RDD(Responsibility-Driven-Design)
- 행동이 상태를 결정한다.
- 객체지향은 실계 세계의 모방이 아닌 은유(Metaphor) 하는 것.
3장: 타입과 추상화
복잡성의 총체인 현실이라는 괴물을 그대로 수용하기에 인간이 지닌 인지 능력과 저장 공간은 너무나도 보잘 것 없다.
- 따라서 본능적으로 이해하기 쉽고 예측가능한 수준으로 현실을 분해하고 단순화하는 전략을 따른다 -> 추상화
추상화의 목적
- 추상화의 목적은 불필요한 부분을 무시함으로써 현실에 존재하는 복잡성을 극복하는 것.
- 훌륭한 추상화 == 목적에 부합하는 것.
현상은 복잡하다. 법칙은 단순하다. 버릴게 무엇인지 알아내라.
타입 = 개념
- 타입 시스템의 목적은 데이터가 잘못 사용되지 않도록 제약사항을 부과하는 것.
- 객체의 타입을 결정하는 것은 객체의 행동 뿐이다.
4장: 역할, 책임, 협력
객체지향 설계의 전체적인 품질은 개별 객체의 품질이 아닌 여러 객체들이 모여내 이뤄내는 협력의 품질이 결정한다.
협력
- 객체의 세계에서도 협력이라는 문맥이 객체의 행동을 결정한다.
- 협력이라는 문맥을 고려하지 않은 채 객체가 가져야 할 상태와 행동부터 고민하기 시작하는 것은 객체지향 입문자들이 자주하는 실수다.
- 협력이 자리를 잡으면 저절로 객체의 행동이 드러나고 뒤이어 객체의 적절한 상태가 결정된다.
책임
객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것이다.
- 객체와 책임이 이리저리 부유하는 상황에서 성급하게 구현에 뛰어드는 것은, 변경에 취약하고 다양한 협력에 참여할 수 없는 비자율적인 객체를 낳게 된다.
- 객체의 책임은 무엇을 알고 있는가?(아는 것) / 무엇을 할 수 있는가?(하는 것) 로 나뉜다.
- 책임은 객체 외부에 제공해 줄 수 있는 정보와 객체 외부에 제공해줄 수 있는 서비스의 목록이다.
역할
본질적으로 역할은 다른 객체에 의해 대체 가능함을 의미한다.
- 역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다.
- 역할을 대체할 수 있는 객체는 '동일한 메시지'를 이해할 수 있는 객체로 한정된다.
- 역할의 개념을 사용하면 유사한 협력을 추상화해서 인지 과부하를 줄일 수 있다. 이는 다양한 객체가 협력에 참여 가능함을 의미한다.
협력을 설계한다는 것은 설계에 참여하는 객체들이 주고 받을 요청과 응답의 흐름을 결정하는 것.
협력을 설계하는 순서
- 책임 할당(요청/응답 흐름 결정)
- 외부에 제공할 행동 결정
- 행동에 필요한 상태(데이터) 정의
- 클래스 구현 방법 결정(실제 구현)
객체지향 설계 기법의 종류
- 책임-주도-설계 RDD(Responsibility-Driven-Design): 협력에 필요한 책임을 식별하고 적합한 객체에게 책임을 할당하는 방식.
- 디자인 패턴(Design Pattern): 특정 문제를 해결하기 위해 이미 식별해놓은 역할/책임/협력의 모음을 활용하는 방식.
- 테스트-주도-개발 TDD(Test-Driven-Development): 테스트를 먼저 작성하고 통과하는 구체적은 코드를 추가하는 방식.
책임-주도-설계 RDD 방법
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
- 시스템 책임을 더 작은 책임으로 분할
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임 할당
- 객체가 책임 수행 중 다른 객체의 도움이 필요한 경우, 이를 책임질 적절한 객체 또는 역할 찾기
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체 협력하도록 설계.
5장: 책임과 메시지
자율적인 책임의 특징은 '어떻게' 해야하는가가 아니라 '무엇을' 해야하는가를 설명해야 한다는 것이다.
메시지
메시지는 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다.
- 객체는 메시지를 처리하기 위한 방법을 자율적으로 선택 가능하다.
다형성
다형성은 객체들의 대체가능성을 이용해 설계를 유연하고 재사용 가능하게 만든다.
- 다형성을 사용하면 송신자가 수신자의 종류를 몰라도 메시지 전송이 가능하다. 즉, 수신자의 종류를 캡슐화 할 수 있다.
- 다형성은 송신자와 수신자 간의 객체 타입에 대한 결합도를 메시지에 대한 결합도로 낮춤으로써 달성한다.
- 이를 통해 유연하고 확장 가능하고 재사용성이 높은 객체를 만든다.
What/Who 사이클
- 어떤 행위(What)를 수행할 지 결정 후
- 누가(Who) 그 행위를 수행할 지 결정
- 객체가 어떤 메시지를 수신하고 처리할 수 있느냐가 객체의 책임을 결정한다.
메시지를 중심으로 설계하라, 자율적인 책임은 저절로 따라올 것이다.
인터페이스
- 객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성된다.
객체지향적인 사고방식을 이해하기 위한 것들
- 좀 더 추상적인 인터페이스: 수신자의 자율성 보장
- 최소 인터페이스: 외부에서 사용할 필요가 없으면 최대한 노출 X
- 인터페이스와 구현 간의 차이가 있다는 점을 인식
구현
- 구현이란 객체를 구성하지만 공용 인터페이스에 포함되지 않은 모든 것을 의미한다.
- 상태, 행동 등은 공용 인터페이스에 포함되지 않는다. 즉, 구현의 영역이다.
- 인터페이스와 구현을 분리하기 위한 설계 방법을 캡슐화라고 한다.
- 구현을 변경할 때 외부에 대한 파급효과를 최소화하기 위해서는 외부의 객체는 공용 인터페이스에만 의존해야하고 구현 세부사항에 대해서는 직접적으로 의존해서는 안된다.
훌륭한 객체란 '구현'을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 뜻한다.
전통적인 개발 vs 객체지향 개발
- 과거의 전통적인 개발 방법은 데이터(상태)와 프로세스(행동)를 엄격하게 구분한다.
- 객체지향 개발 방법은 데이터, 프로세스를 객체라는 하나의 틀 안으로 묶음으로서 객체의 자율성을 보장한다.
- 이는 전통적인 개발 방법과 객체지향을 구분짓는 가장 중요한 차이이다.
책임이 얼마나 자율적인지가 전체적인 협력의 설계 품질을 결정하게 된다.
책임이 자율적이어야하는 이유 5가지
- 자율적인 책임은 협력을 단순하게 만든다.
- 자율적인 책임은 객체의 외부와 내부를 명확하게 분리한다.
- 책임이 자율적일 경우, 책임을 수행하는 내부적인 방법을 경해도 외부에 영향을 미치지 않는다.
- 자율적인 책임을 협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다.
- 객체가 수행하는 책임들이 자율적일수록 객체의 역할을 이해하기 쉬워진다.
6장: 객체 지도
객체지향은 자주 변경되는 기능이 아니라 안정적인 구조를 기반으로 시스템을 구조화한다.
두가지 재료: 구조 & 기능
- 구조는 사용자나 이해관계자들이 도메인에 관해 생각하는 개념과 개념들 간의 관계로 표현한다. → 도메인 모델링
- 기능은 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템으이 행위로 표현한다. → 유스케이스 모델링
도메인?
도메인 모델을 기반으로 코드를 작성하는 이유
- 도메인 모델이 제공하는 구조가 상대적으로 안정적이기 때문.
- 사용자 모델에 포함된 개념과 규칙은 비교적 변경될 확률이 적기 때문.
도메인 모델은 기능을 구현할 때 참조할 수 있는 궁극적인 지도이다.
유스케이스?
- 사용자의 목표를 달성하기 위해 사용자와 시스템 간에 이뤄지는 상호작용의 흐름을 텍스트로 정리한 것.
- 사용자 목표가 유스케이스의 핵심이다.
- 유스케이스는 공통의 사용자 목표를 통해 강하게 연관된 시나리오의 집합이다.
유스케이스의 특성
- 유스케이스는 사용자와 시스템 간의 상호작용을 보여주는 '텍스트' (일련의 이야기 흐름)
- 유스케이스는 하나의 시나리오가 아닌 여러 시나리오들의 집합이다.
- 유스케이스는 단순한 feature 목록과 다르다. → 연관된 기능들을 묶어줌.
- 유스케이스는 사용자 인터페이스와 관련된 세부정보를 포함하지 말아야 한다. → 시스템 행위에 초점 맞추기.
- 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다. → 연관된 기능을 이야기 형식으로 모으는 것이 주 목적.
유스케이스는 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공한다.
도메인 모델이 안정적인 이유
- 도메인 모델을 구성하는 개념은 비즈니스가 없어지거나 완전히 개편되지 않는 한 안정적으로 유지된다.
- 도메인 모델을 구성하는 개념 간의 관계는 비즈니스 규칙을 기반으로 하기 때문에 정책이 크게 변경되지 않는 한 안정적으로 유지된다.
7장: 함께 모으기
인터페이스와 구현을 분리하라!