객체지향의 사실과 오해(feat. 토끼책 🐰)를 읽으며 메모했던 내용 정리

thinkySide·2024년 3월 31일
1
post-thumbnail

🧐 책을 읽게 된 이유

그 유명한 토끼책을 추천 받아 읽게 되었다. '객체지향'이라는 단어와 언어(Swift)를 자연스럽게 사용하면서도 정작 그 의미에 대해 잘 알지 못했었는데, 멋있는 분이 추천해주셔서 마음 먹을 수 있게 되었다. 원래 구매해서 읽으려고 했는데 운이 좋게도 아카데미 기숙사 룸메이트가 마침 가지고 있어 빌려 읽을 수 있었다!

🐰 느낀점 적어보기

객체지향의 근본적인 의미를 이해하는 것이 왜 어려운지 알게 되었다. 객체지향을 설명할 때 '실제 세계의 모방'을 예시로 들곤 하는데, 이 때문에 내가 원래 가지고 있던 관념과 연결시키기 어려웠던 것 같기도 하다. (실제 세계를 모방하다보면 이해할 수 없는 것들이 많기 때문에) 이 책에서 객체지향은 '실제 세계의 은유'라는 표현을 사용한다. 그리고 새로운 세계를 창조하는 것이라 이야기 한다. 이 문장이 저자가 말하고자 하는 핵심이 아닐까 싶다.

개발적인 관점 외에서도 재밌는 부분이 많았다. '개념'이라던가, '추상화' 등 이 세상을 이해할 수 있게 돕는 다양한 예시들이 기억에 남는다. 결국 객체지향 패러다임이 주목받게 된 이유도 불완전한 우리들 때문일 것이다. (AI에게는 객체지향 프로그래밍이 정말 비효율적이고 이해할 수 없을수도 있지 않을까?)

결론적으로 이 책을 읽고 나의 하드스킬이 성장했다 보기에 어려움이 있을 수 있겠지만, 시야가 넓어진 것은 확실하다. (서문에서도 이게 목적이라고 했었는데 완전 성공이다) 왜 객체지향이라는 개념이 등장했고, 왜 객체지향 프로그래밍이 중요한지 이해할 수 있게 만들어준 고마운 책이다.

1장: 협력하는 객체들의 공동체

객체 지향의 첫걸음

  1. 클래스가 아닌 '객체'를 바라보는 것.
  2. 객체를 독립적인 존재가 아닌 기능 구현을 위해 협력하는 공동체의 존재로 바라보는 것.
  3. 협력에 참여하는 객체들에게 얼마나 적절한 역할과 책임을 부여할 수 있는가?
  4. 위 3가지 개념을 프로그래밍 언어에 담아낼 수 있는 기술을 익히는 것.

객체 지향의 목표는 실세계를 모방하는 것이 아니다. 오히려 새로운 세계를 창조하는 것이다.

역할, 책임, 협력

객체지향 설계는 적절한 객체에게 적절한 책임을 할당하는 것에서 시작된다."

  • 역할은 관련성 높은 책임의 집합이다."
  • 협력의 성공은 특정한 역할을 맡은 각 개인(객체)이 얼마나 요청을 성실히 이행하는가에 달렸다.
  • 특정한 역할은 특정한 책임을 암시한다.
  • 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력 -> 다형성(polymorphism)

객체가 갖춰야 할 2가지 덕목

  1. 객체는 충분히 협력적이어야 한다.
    • 객체는 다른 객체의 요청에 귀기울이고
    • 다른 객체에게 적극적으로 도움을 요청할 열린 마음을 지녀야 한다.
    • 외부의 도움을 무시한 채 모든 것을 스스로 처리하려고하는 전지전능한 객체는 내부의 복잡도에 의해 자멸할 수 있다.
  2. 객체는 충분히 자율적이어야한다.
    • 객체는 다른 객체의 명령에 복종하는 것이 아닌, 요청에 응답할 뿐이다.
    • 어떤 방식으로 응답할지는 객체 스스로가 판단 후 결정한다.
    • 요청에 응할지 여부도 스스로 결정 가능하다.

객체는 다른 객체가 무엇을(What) 수행하는지는 알 수 있지만, 어떻게(How) 수행하는 지에 대해서는 알 수 없다.

그래서 객체지향이 뭔데?

시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법

2장: 이상한 나라의 객체

세상을 더 작은 객체로 분해하는 것은 본질적으로 세상이 포함하고 있는 복잡성을 극복하기 위한 인간의 작은 몸부림이다.

상태, 행동, 식별자

  • 상태를 이용하면 과거의 모든 행동 이력을 설명하지 않고도 행동의 결과를 예측 및 설명할 수 있다.
  • 모든 객체의 상태는 단순한 값과 객체의 조합으로 표현할 수 있다.
  • 객체는 상태를 캡슐 안에 감춰둔채 외부로 노출하지 않는다. 객체가 외부에 노출하는 것은 행동뿐이며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동 뿐이다.

객체 지향 초보가 가장 쉽게 빠지는 함정

❌ 상태를 먼저 결정하고 행동을 나중에 결정하는 것

  • 상태가 아닌 행동에 초점을 맞춰야 한다.
  • 어떤 책임이 필요한가를 결정하는 과정이 전체 설계를 주도해야 한다 -> RDD(Responsibility-Driven-Design)
  • 행동이 상태를 결정한다.
  • 객체지향은 실계 세계의 모방이 아닌 은유(Metaphor) 하는 것.

3장: 타입과 추상화

복잡성의 총체인 현실이라는 괴물을 그대로 수용하기에 인간이 지닌 인지 능력과 저장 공간은 너무나도 보잘 것 없다.

  • 따라서 본능적으로 이해하기 쉽고 예측가능한 수준으로 현실을 분해하고 단순화하는 전략을 따른다 -> 추상화

추상화의 목적

  • 추상화의 목적은 불필요한 부분을 무시함으로써 현실에 존재하는 복잡성을 극복하는 것.
  • 훌륭한 추상화 == 목적에 부합하는 것.

현상은 복잡하다. 법칙은 단순하다. 버릴게 무엇인지 알아내라.

타입 = 개념

  • 타입 시스템의 목적은 데이터가 잘못 사용되지 않도록 제약사항을 부과하는 것.
  • 객체의 타입을 결정하는 것은 객체의 행동 뿐이다.

4장: 역할, 책임, 협력

객체지향 설계의 전체적인 품질은 개별 객체의 품질이 아닌 여러 객체들이 모여내 이뤄내는 협력의 품질이 결정한다.

협력

  • 객체의 세계에서도 협력이라는 문맥이 객체의 행동을 결정한다.
  • 협력이라는 문맥을 고려하지 않은 채 객체가 가져야 할 상태와 행동부터 고민하기 시작하는 것은 객체지향 입문자들이 자주하는 실수다.
  • 협력이 자리를 잡으면 저절로 객체의 행동이 드러나고 뒤이어 객체의 적절한 상태가 결정된다.

책임

객체지향 개발에서 가장 중요한 능력은 책임을 능숙하게 소프트웨어 객체에 할당하는 것이다.

  • 객체와 책임이 이리저리 부유하는 상황에서 성급하게 구현에 뛰어드는 것은, 변경에 취약하고 다양한 협력에 참여할 수 없는 비자율적인 객체를 낳게 된다.
  • 객체의 책임은 무엇을 알고 있는가?(아는 것) / 무엇을 할 수 있는가?(하는 것) 로 나뉜다.
  • 책임은 객체 외부에 제공해 줄 수 있는 정보와 객체 외부에 제공해줄 수 있는 서비스의 목록이다.

역할

본질적으로 역할은 다른 객체에 의해 대체 가능함을 의미한다.

  • 역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다.
  • 역할을 대체할 수 있는 객체는 '동일한 메시지'를 이해할 수 있는 객체로 한정된다.
  • 역할의 개념을 사용하면 유사한 협력을 추상화해서 인지 과부하를 줄일 수 있다. 이는 다양한 객체가 협력에 참여 가능함을 의미한다.

협력을 설계한다는 것은 설계에 참여하는 객체들이 주고 받을 요청과 응답의 흐름을 결정하는 것.

협력을 설계하는 순서

  1. 책임 할당(요청/응답 흐름 결정)
  2. 외부에 제공할 행동 결정
  3. 행동에 필요한 상태(데이터) 정의
  4. 클래스 구현 방법 결정(실제 구현)

객체지향 설계 기법의 종류

  1. 책임-주도-설계 RDD(Responsibility-Driven-Design): 협력에 필요한 책임을 식별하고 적합한 객체에게 책임을 할당하는 방식.
  2. 디자인 패턴(Design Pattern): 특정 문제를 해결하기 위해 이미 식별해놓은 역할/책임/협력의 모음을 활용하는 방식.
  3. 테스트-주도-개발 TDD(Test-Driven-Development): 테스트를 먼저 작성하고 통과하는 구체적은 코드를 추가하는 방식.

책임-주도-설계 RDD 방법

  1. 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악
  2. 시스템 책임을 더 작은 책임으로 분할
  3. 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임 할당
  4. 객체가 책임 수행 중 다른 객체의 도움이 필요한 경우, 이를 책임질 적절한 객체 또는 역할 찾기
  5. 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체 협력하도록 설계.

5장: 책임과 메시지

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

메시지

메시지는 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 의사소통 수단이다.

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

다형성

다형성은 객체들의 대체가능성을 이용해 설계를 유연하고 재사용 가능하게 만든다.

  • 다형성을 사용하면 송신자가 수신자의 종류를 몰라도 메시지 전송이 가능하다. 즉, 수신자의 종류를 캡슐화 할 수 있다.
  • 다형성은 송신자와 수신자 간의 객체 타입에 대한 결합도를 메시지에 대한 결합도로 낮춤으로써 달성한다.
  • 이를 통해 유연하고 확장 가능하고 재사용성이 높은 객체를 만든다.

What/Who 사이클

  1. 어떤 행위(What)를 수행할 지 결정 후
  2. 누가(Who) 그 행위를 수행할 지 결정
  • 객체가 어떤 메시지를 수신하고 처리할 수 있느냐가 객체의 책임을 결정한다.

메시지를 중심으로 설계하라, 자율적인 책임은 저절로 따라올 것이다.

인터페이스

  • 객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성된다.

객체지향적인 사고방식을 이해하기 위한 것들

  • 좀 더 추상적인 인터페이스: 수신자의 자율성 보장
  • 최소 인터페이스: 외부에서 사용할 필요가 없으면 최대한 노출 X
  • 인터페이스와 구현 간의 차이가 있다는 점을 인식

구현

  • 구현이란 객체를 구성하지만 공용 인터페이스에 포함되지 않은 모든 것을 의미한다.
  • 상태, 행동 등은 공용 인터페이스에 포함되지 않는다. 즉, 구현의 영역이다.
  • 인터페이스와 구현을 분리하기 위한 설계 방법을 캡슐화라고 한다.
  • 구현을 변경할 때 외부에 대한 파급효과를 최소화하기 위해서는 외부의 객체는 공용 인터페이스에만 의존해야하고 구현 세부사항에 대해서는 직접적으로 의존해서는 안된다.

훌륭한 객체란 '구현'을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 뜻한다.

전통적인 개발 vs 객체지향 개발

  • 과거의 전통적인 개발 방법은 데이터(상태)와 프로세스(행동)를 엄격하게 구분한다.
  • 객체지향 개발 방법은 데이터, 프로세스를 객체라는 하나의 틀 안으로 묶음으로서 객체의 자율성을 보장한다.
  • 이는 전통적인 개발 방법과 객체지향을 구분짓는 가장 중요한 차이이다.

책임이 얼마나 자율적인지가 전체적인 협력의 설계 품질을 결정하게 된다.

책임이 자율적이어야하는 이유 5가지

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

6장: 객체 지도

객체지향은 자주 변경되는 기능이 아니라 안정적인 구조를 기반으로 시스템을 구조화한다.

두가지 재료: 구조 & 기능

  • 구조는 사용자나 이해관계자들이 도메인에 관해 생각하는 개념과 개념들 간의 관계로 표현한다. → 도메인 모델링
  • 기능은 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템으이 행위로 표현한다. → 유스케이스 모델링

도메인?

  • 사용자가 프로그램을 사용하는 대상 분야

도메인 모델을 기반으로 코드를 작성하는 이유

  • 도메인 모델이 제공하는 구조가 상대적으로 안정적이기 때문.
  • 사용자 모델에 포함된 개념과 규칙은 비교적 변경될 확률이 적기 때문.

도메인 모델은 기능을 구현할 때 참조할 수 있는 궁극적인 지도이다.

유스케이스?

  • 사용자의 목표를 달성하기 위해 사용자와 시스템 간에 이뤄지는 상호작용의 흐름을 텍스트로 정리한 것.
  • 사용자 목표가 유스케이스의 핵심이다.
  • 유스케이스는 공통의 사용자 목표를 통해 강하게 연관된 시나리오의 집합이다.

유스케이스의 특성

  1. 유스케이스는 사용자와 시스템 간의 상호작용을 보여주는 '텍스트' (일련의 이야기 흐름)
  2. 유스케이스는 하나의 시나리오가 아닌 여러 시나리오들의 집합이다.
  3. 유스케이스는 단순한 feature 목록과 다르다. → 연관된 기능들을 묶어줌.
  4. 유스케이스는 사용자 인터페이스와 관련된 세부정보를 포함하지 말아야 한다. → 시스템 행위에 초점 맞추기.
  5. 유스케이스는 내부 설계와 관련된 정보를 포함하지 않는다. → 연관된 기능을 이야기 형식으로 모으는 것이 주 목적.

유스케이스는 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공한다.

도메인 모델이 안정적인 이유

  • 도메인 모델을 구성하는 개념은 비즈니스가 없어지거나 완전히 개편되지 않는 한 안정적으로 유지된다.
  • 도메인 모델을 구성하는 개념 간의 관계는 비즈니스 규칙을 기반으로 하기 때문에 정책이 크게 변경되지 않는 한 안정적으로 유지된다.

7장: 함께 모으기

인터페이스와 구현을 분리하라!

profile
UX 한스푼 넣은 iOS 디발자 한톨 / Apple Developer Academy @POSTECH 3기 / 티스토리 이사중,, https://thinkyside.tistory.com/

0개의 댓글

관련 채용 정보