IT Paradigm/OOP

객체지향의 사실과 오해 독서 후기 (feat. OOP)

yeTi 2023. 4. 17. 12:32

안녕하세요. yeTi입니다.
오늘은 객체지향의 사실과 오해 읽은 후기를 기록하려고 합니다.

책을 선택한 이유

OOP 에 대해 알고 싶은 마음에 개념의 창시자이신 Alen Kay 님의 The early history of SmaillTalk 을 읽고 깊은 감명을 받아 기록으로 남겼습니다. OOP의 기원 (feat. 객체와 메시지)

이 후 조용호 님의 오브젝트 를 읽어볼까 하다가. 뭔가 상세한 지식을 접하기 전에 객체메시지에 대한 시야를 넓히고 싶다는 생각에 객체지향의 사실과 오해 를 선택했습니다.

저는 책의 서문을 주의깊게 읽는 스타일인데요.

조용호 님이 이 책을 집필하신 동기나 목적이 제가 알고 싶어하는 부분과 일맥상통(一脈相通) 하다는 느낌을 받아 이 책을 잘 선택했다는 기쁨을 느낄 수 있었습니다.

신세계를 창조하라

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

첫 장의 첫 주제부터 오해를 하고 있는 부분이 밝혀지고 있습니다.

객체지향 은 현실 세계를 설계에 잘 projection 하는가에 대한 능력의 차이라고 생각하고 있었습니다.

그런데 고객과 사용자를 만족 시킬 수 있는 신세계를 창조하는 것이라니..

안영회 대표님께서 사용하신 용어가 생각나는 순간이었습니다.

I define...

책에서는 이상한 나라의 앨리스를 은유(metaphor) 하여 자율적인 객체에 대해 설명합니다.

현실 속의 트럼프 카드는 스스로 뒤집을 수도, 말을 할 수도, 걸을 수도 없다. 현실 세계의 채셔 고양이는 웃을 수 없다. 현실 속의 토끼는 두 발로 서서 시계를 보며 허둥지둥 뛰어다니지 않는다. 전화기는 스스로 통화 버튼을 누를 수 없으며 계좌는 스스로 금액을 이체할 수 없다. 스스로 판매 금액을 계산해서 종이에 기입하는 현실 속의 상품을 상상해 보라. 여러분이 객체지향 세계를 구축할 때 현실에서 가져온 객체들은 현실 속에서는 할 수 없는 어떤 일이라도 할 수 있는 전지전능한 존재가 된다. - p.67

현실을 그대로 투영한 객체를 만들 수 없다는 비유입니다. 현실에서 보다 객체가 가지는 자율성이 높고, 객체의 자율성이 높을 때 객체간 긴밀한 협력을 만들 수 있다고 말합니다.

이러한 개념이 신세계를 창조하라는 말의 기반이 됩니다.

클래스 관점에서 객체 관점으로 사고를 전환하라.

훌륭한 객체지향 설계자가 되기 위해 거쳐야 할 첫 번째 도전은 코드를 담는 클래스의 관점에서 메시지를 주고받는 객체의 관점으로 사고의 중심을 전환하는 것이다. - p.38

그 동안 객체클래스는 완전히 대응하는 것으로 인식하고 있는 상태에서 클래스 관점에서 객체 관점으로 사고를 전환한다는 것이 어떤 것을 의미하는지 와닿지 않았습니다.

그런데 실무에서 사고하는 방식을 생각해보면 묘한 차이점을 느낄 수 있습니다.

우리(초보 객체지향러)가 클래스를 만들 때 참조하는 것은 무엇인가요? 신세계를 창조하시나요? 아니면 ERD의 테이블을 copy 하시나요?

또, 그렇게 만들어진 클래스의 상태(혹은 멤버 변수)에 관심이 많으신가요? 아니면 클래스간 협력에 대해 관심이 많으신가요?

혹시 클래스를 만들 때 ERD의 테이블을 copy하고, 클래스에 setter를 거부감없이 정의하여 사용하고 계시다면 클래스 관점에서 사고하고 계신겁니다.

상세한 내용은 아래의 내용들을 천천히 살퍄보시면 이해의 폭을 넓히실 수 있을겁니다.

송신자는 메시지 수신자의 상태 변경에 대해서는 전혀 알지 못한다.

두 메시지를 보고 엘리스의 키가 줄어든다거나 음료의 양이 줄어든다는 상태 변경을 예상할 수 있겠는가? 메시지를 엘리스에게 전송하는 객체이건 음료에게 메시지를 전송하는 엘리스 객체이건 메시지 송신자는 메시지 수신자의 상태 변경에 대해서는 전혀 알지 못한다. - p.56

두 가지의 공식이 떠올랐습니다.

  • JPA 에서 엔티티 에 setter를 제거하고 도메인 행위를 정의하라.
  • 관심사를 분리 하라.

setter를 제거하는 이유는 객체의 행위를 표현하여 코드 가독성을 높이고 상태 변경에 대해 예측성을 높이기 위함이라고 이해를 하고 있었고, 관심사의 분리 또한 개발자의 관점에서 목적을 명확히 하고 코드를 응집하여 코드의 가독성을 높이기 위한 활동으로 인식하고 있었습니다.

글을 읽으면서 문득. 클래스라는 형태로 데이터를 표현해왔던 것이라는 생각이 들었습니다.

그러다보니 클래스 변수를 setter로 변환하는 것이 당연하게 받아들여지고 관심사의 분리가 객체 중심이 아닌 클래스 중심으로 이뤄지는게 자연스러운 행위로 인식된다는 생각이 들었습니다.

관심사의 분리도 메시지의 송신자의 관점이 아니고 데이터의 필요에 의해서 나눠왔다는 생각이 들었습니다.


인용문에서 의미하는 객체상태행동 의 관점에서 다시 본다면,

엔티티에서 setter를 제거하는 것은 객체 관점에서 행위를 표현하라는 의미였고 (데이터를 변경하고 싶어 함수를 찾지 말고, 객체에게 메시지 를 전달하라.)

관심사의 분리는 클래스의 관점에서 데이터 경계를 설정하는 것이 아니고 객체 관점에서 메시지를 전달하라는 의미였다는것을 깨달았던 순간입니다.

수신자의 상태에 관심을 꺼주세요.

초보: 상태를 중심으로 객체를 바라보는 행동

객체지향에 갓 입문한 사람들이 가장 쉽게 빠지는 함정은 상태를 중심으로 객체를 바라보는 것이다. 초보자들은 먼저 객체에 필요한 상태가 무엇인지를 결정하고 그 상태에 필요한 행동을 결정한다. 애플리케이션 안에 살아갈 앨리스 객체를 설계할 때 초보자들은 앨리스 객체에게 필요한 상태가 무엇인지를 찾고 키와 위치를 앨리스에 추가한다. 그리고 나서야 키와 위치를 변경하거나 조회할 수 있는 행동이 무엇인지를 고민한다. - p.64

아직 객체지향초보자 라는 것을 인지시켜주는 문단입니다.

상태를 중심으로 객체를 바라보는 행동은 앞서 언급한 데이터기반 클래스 설계하기의 모습과 동일합니다.

그러다보니 행동을 설계하는 것이 어색하고 필요성에 대해 공감하기도 어렵다고 느끼는거 같습니다.

앞서 소제목으로 언급했던 클래스 관점에서 객체 관점으로 사고를 전환하라. 에서 클래스를 설계한 때 ERD를 언급한 이유가 여기에 있습니다.

제가 흔하게 봐왔던 클래스 설계 방식은 데이터기반 클래스 설계하기 였습니다.

객체지향은 사람의 인지능력을 활용하여 시스템을 구조화하는 개념이다.

객체는 소중하다. 따라서 소중한 객체를 안전하고 적절한 장소에 보관할 수 있도록 여러분의 인지능력을 발휘해 최대한 직관적으로 분류하라. - p.87

자율적인 객체 라는 개념을 사용하면서 객체라는 세상을 정의하는 방식을 채용하는 궁극적인 목적이 사람의 인지능력의한계 때문이라는 생각이 든 문단입니다.

객체지향 은 사람의 인지능력을 활용하여 시스템을 구조화하는 개념을 채용했습니다. 하지만 사람의 인지능력에는 명확한 한계가 있기 때문에 안전하고 적절한 장소에 보관하기 위해서는 객체를 정의할 때 혼란이 없도록 직관적으로 정의해야 합니다.

책임-주도 설계라고 부르는 객체지향 설계 방법은 데이터를 먼저 생각하는 데이터-주도 설계 방법의 단점을 개선하기 위해 고안됐다. - p.94

앞서 지속적으로 언급했던 데이터기반 클래스 정의라는 저의 생각을 신뢰있는 워딩으로 치환할 수 있는 단어가 나타났습니다.

데이터-주도 설계

책임-주도 설계 방법이란 객체가 외부에 제공해야하는 책임을 행동으로 먼저 정의하고 책임을 수행하는데 필요한 데이터를 나중에 정의하는 방식의 캡슐화 를 의미합니다.

또한 다형성 의 개념도 제 오해를 풀어주는데요.

상태기반 다형성 설계. 흔히 자바 에서 상속 으로 정의하는 구조는 객체의 분류 체계를 급격히 위험에 노출시켜 유연하지 못한 설계를 낳습니다.

이를 동일한 메시지를 처리할 수 있는 행동기반 다형성 설계. 자바에서 인터페이스 를 implements하는 구조가 동일한 메시지를 객체가 자유롭게 처리할 수 있는 유연성 을 가집니다.

결국 객체지향에서 중요한 것은 동적으로 변하는 객체의 '상태'와 상태를 변경하는 '행위'다. 클래스는 타입을 구현하기 위해 프로그래밍 언어에서 제공하는 구현 메커니즘이라는 사실을 기억하라. - p.105

앞서 객체지향 설계자의 역량 중 하나는 코드를 담는 클래스의 관점에서 메시지를 주고 받는 객체의 관점으로 사고의 중심을 전환하는 것이라고 했습니다.

클래스 라는 것은 타입 을 구현하기 위한 메커니즘 의 하나일 뿐인데 그것이 객체인양 인식하는 것에 대한 오해를 풀어주기 위한 말이었다는 생각이 듭니다.

훌륭한 객체지향 설계란 협력적인 객체를 창조하는 것이다.

훌륭한 객체지향 설계란 겉모습은 아름답지만 협력자들을 무시하는 오만한 객체를 창조하는 것이 아니라 조화를 이루며 적극적으로 상호작용하는 협력적인 객체를 창조하는 것이다. 비록 그 객체를 따로 떼어놓고 봤을 때는 겉모습이 다소 기묘하고 비합리적이더라도 말이다. - p.109

훌륭한 객체지향 설계협력적인 객체창조 하는 것이라고 한번 더 강조합니다.

오만한 객체라는 자기 중심적인 객체를 지양하고 적극적으로 객체간 메시지로 상호작용하는 협력적인 객체를 창조해야 한다는 말입니다.

책임을 어떻게 구현할 것인가 하는 문제는 객체와 책임이 제자리를 잡은 후에 고려해도 늦지 않다. 객체와 책임이 이리저리 부유하는 상황에서 성급하게 구현에 뛰어드는 것은 변경에 취약하고 다양한 협력에 참여할 수 없는 비자율적인 객체를 낳게 된다. - p.115

책임 은 어떤 객체가 어떤 요청에 대해 대답해 줄 수 있거나, 적절한 행동을 할 의무가 있다는 것을 의미합니다.

이러한 책임이 적절하게 객체에 할당하기 전에 구현하면 비자율적인 객체를 낳을 수 있기 때문에 지양하라고 합니다.

이는 TDD에서 말하는 가장 간단한 구현과 상충된다고 생각하기 때문에 시간을 두고 이해의 폭을 넓힐 필요가 있습니다.

요약하면 역할의 개념을 사용하면 유사한 협력을 추상화해서 인지 과부하를 줄일 수 있다. 또한 다양한 객체들이 협력에 참여할 수 있기 때문에 협력이 좀 더 유연해지며 다양한 객체들이 동일한 협력에 참여할 수 있기 때문에 재사용성이 높아진다. 역할은 객체지향 설계의 단순성(simplicity), 유연성(flexibility), 재사용성(reusability)을 뒷받침하는 핵심 개념이다. - p.126

앞서 객체지향을 하는 이유는 사람의 인지능력을 활용하기 위한 것이라고 얘기했습니다.

동일하게 협력추상화 할 수 있는 역할 의 개념을 사용함으로 인해서 인지능력을 보다 효율적으로 사용할 수 있습니다.

그리고 재사용성 이라는 개념을 자바 문법의 상속 , 인터페이스 , 제네릭 과 같이 문법에 종속한 것이 아닌 역할의 표현이라는 측면으로 이해하는 것이 보다 코드를 이해하는 시야를 넓히는데 도움이 된다고 생각합니다.

또한 역할이라는 개념하에 다양한 객체들이 동일한 협력에 참여할 수 있기 때문에 단순성 , 유연성 , 재사용성 을 높일 수 있다고 말합니다.

이는 지난날 자바의 상속의 장점으로 배운 유연성, 재사용성이 국소적인 개념으로 배웠다는 점을 인지할 수 있는 계기가 됐습니다.

동일한 메시지에 대해 서로 다르게 반응하는 것이 다형성이다.

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

다형성 에 대한 오해가 또 발견됐다.

다형성이란 자바상속 을 활용하여 공통 변수와 함수를 관리할 수 있는 기법으로 다양한 타입으로 확장하여 사용하는 것으로 이해하고 있었습니다.

여기서 중요한 것은 공통의 변수였습니다.

그러나.

동일한 메시지에 대해 서로 다르게 반응하는 것을 다형성이라고 책에서는 말합니다.

그렇다면 중요한 것은 동일한 메시지라는 것이고. 이는 상태가 같은 것이 있던 없던 온전한 수신자 객체의 몫이라는 것입니다.

그렇다면 이는 앞서 말했던 역할을 부여하는 것도 다형성의 일부분으로 보여집니다. 왜냐하면 역할이란 동일한 메시지 (책임)을 이해할 수 있는 객체의 집단이라고 표현했기 때문입니다.

즉, 객체는 다양한 역할을 가질 수 있다는 말인데. 자바에서 다양한 행동을 부여할 수 있는 것은 상속이 아닌 인터페이스입니다.

결국 그 동안 알고 있었던 다형성은 상속이라는 개념이 다형성인터페이스 라고 바뀌어야한다는 생각이 듭니다.

인간의 두뇌가 한 번에 생각할 수 있는 양으로 조절하는 것입니다.

인간의 두뇌가 한 번에 생각할 수 있는 양에는 한계가 있으므로 변경이라는 강력한 적과의 전쟁에서 승리하기 위해 인간이 취할 수 있는 마지막 생존 전략은 변경해도 무방한 안전 지대와 변경했을 경우 외부에 영향을 미치는 위험 지대를 구분하는 것이다. 여기서 안전 지대가 내부인 객체의 구현이고 위험 지대가 외부인 객체의 공용 인터페이스다. - p.169

인간의 한계에 따라 체계적으로 지식체계를 관리하지 않는다면 사용할 수 없는 지식으로 변모한다는 것입니다.

객체지향 에서는 지식의 체계를 객체라는 경계로 캡슐화 하여 인지 능력을 최대한으로 활용하고 최소한의 인터페이스를 노출하여 위험 지대를 최소한으로 관리하여 인간의 두뇌가 한 번에 생각할 수 있는 양으로 조절하는 것입니다.

인터페이스와 구현의 분리 원칙은 변경을 관리하기 위한 것이다. 좀 더 고급스럽게 말하면 송신자와 수신자가 구체적인 구현 부분이 아니라 느슨한 인터페이스에 대해서만 결합되도록 만드는 것이다. - p.170

결국 송/수신자간 loose coupling 을 달성하기 위한 것이 인터페이스구현분리 입니다.

객체를 지향하라.

이 책을 읽는 목적이 여기에서 나옵니다.

객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다. - p.38

함께 읽기 모임에서 2장의 앨리스를 모델링하고 리뷰하는 과정에서 느낀, 객체를 지향하라의 key는 은유(metaphor)였습니다.

현실을 무시하고 자유롭게 여러분만의 새로운 세계를 창조하기 바란다. 앨리스를 매혹시킨 이상한 나라가 그런 것처럼 말이다. - p.71

주어진 상황이나 조건에 종속되어 클래스화 하는것에 집중하는 것이 아니라. 새로운 세계를 창조은유적으로 표현하는 것이 Object Oriented 의 중심 개념이라고 정리할 수 있었던 시간이었습니다.

초반에 언급한 신세계를 창조하라객체지향의 시놉시스였습니다.

제 경험내에서는 신세계를 창조하는 분들보다 상대의 의도를 파악해가며 현실에서의 데이터를 정리하는 분들을 많이 봐왔습니다.

그래서 객체지향이 어려운 내용들로 가득하도록 만들어져왔다는 생각이 듭니다.

Simple is the best

예전 천동설이 우주를 설명하기 위해 복잡한 이론을 도입해가던 과정에 반하여 지동설은 간결한 모델로 우주를 설명했습니다. 결국 지동설이 진리로 밝혀 졌지요.

TDD에서도 테스트 코드를 구현하기 힘들다는 것은 설계를 의심해보라고 말합니다.

이러한 맥락에서 객체지향을 설명하는 복잡성 증가는 옳은 원리를 바탕으로 설명을 하고 있지에 대한 의심을 해볼만하다고 생각합니다.

저는 객체지향의 간결한 원리는 신세계를 자유롭게 창조하고 인간의 인지능력을 활용하기 위해 은유하라 입니다.

안정적인 구조를 만들라

지도 은유(metaphor)로 나타내고자 했던 맥락은 길을 알고 싶을 때 네비게이션의 결괏값처럼 가고자하는 목적지를 위한 경로 정보는 목적에 종속된다는 것입니다. 목적지만 바뀌어도 정보가 바뀌는 것과 같습니다.

따라서 지도를 가지고 원하는 정보를 찾을 수 있도록 안정적인 구조를 비유합니다.

그리고 안정적인 구조는 갖추기 위한 모델링 패러다임은 객체지향 이 유일하다고 말합니다.

따라서 도메인 모델의 세 가지 측면을 모두 모델링할 수 있는 유사한 모델링 패러다임을 사용할수록 소프트웨어 개발이 쉬워질 것이다. 객체지향은 이런 요구사항을 가장 범용적으로 만족시킬 수 있는 거의 유일한 모델링 패러다임이다. - p.187

또한 안정적인 구조를 만들 수 있는 기반은 도메인 모델 이라고 말합니다. 왜냐하면 기능의 변경은 잦아도 도메인 모델은 비즈니스가 변경되지 않는 한 변경될 가능성이 낮기 때문입니다. (DDD 의 시놉시스??)

변경에 대비하고 변경의 여지를 남겨 놓는 가장 좋은 방법은 자주 변경되는 기능이 아닌 안정적인 구조를 중심으로 설계하는 것이다. - p.183

여기에 사용자들이 이해한 도메인을 유스케이스 를 활용하여 얻음으로써 사용자와 시스템간 멘탈 모델의 간극을 최소화할 수 있습니다.

유스케이스의 가치는 사용자들의 목표를 중심으로 시스템의 기능적인 요구사항들을 이야기 형식으로 묶을 수 있다는 점이다. 산발적으로 흩어져 있는 기능에 사용자 목표라는 문맥을 제공함으로써 각 기능이 유기적인 관계를 지닌 체계를 이룰 수 있게 한다.

이렇게 유스케이스/도메인 모델/객체지향 패러다임이 유기적으로 이뤄진다면 유연하고 유기적인 소프트웨어를 만들 수 있습니다.

요구사항들을 식별하고 도메인 모델을 생성한 후, 소프트웨어 클래스에 메서드들을 추가하고, 요구사항을 충족시키기 위해 객체들 간의 메시지 전송을 정의하라. - p.199

저는 여기에 의견을 하나 첨부하고 싶습니다.

유스케이스의 맥락을 테스트케이스 로 표현해주세요. 왜냐하면 도메인 모델만 보고 모든 맥락을 유추하기란 어렵기 때문이다.

그리고 객체지향 설계의 숙제에 대한 답도 발견했습니다. 객체를 어떻게 식별 하나요? 내 마음입니다.

함께 모으기

마틴 파울러가 말하는 개념 관점, 명세 관점, 구현 관점을 제시합니다.

도메인 을 파악하여 개념을 모델링하고

객체간 협력 관계를 식별하여 인터페이스를 도출하며,

설계 검증을 위해 빠르게 구현하라.


참고적으로 TDD 를 활용하여 협력을 설계하는 것이 개념적으로만 하는 설계보다 직접적 이라고 말합니다.

우리는 현실의 복잡도를 줄이기 위해 추상화를 활용합니다.

추상화란 도메인의 복잡성을 단순화하고 직관적인 멘탈 모델을 만드는데 사용할 수 있는 가장 기본적인 인지 수단이다. 사람들은 도메인에 존재하는 개념들을 구조화하고 단순화하기 위해 다양한 추상화 기법을 사용한다. - p.229

추상화 기법으로 다음 3가지를 제시합니다.

  • 분류와 인스턴스화
  • 일반화와 특수화
  • 집합과 분해

분류와 인스턴스화범주를 정의하고 범주 로 객체를 분류하는 것입니다. 이는 범주로 묶음으로써 세상에 존재하는 복잡성을 낮출 수 있습니다. (타입 의 정의)

일반화와 특수화범주를 계층적으로 구분하는 것입니다. 이는 귀납적 추리에 의해 객체에 대한 추론을 가능하게 합니다. (타입의 #계층화 )

마지막으로 집합과 분해는 외부에서는 전체에 관해서만 알고 있고 내부의 세부 사항에 대해서는 알지 못하게 캡슐화 하는 것입니다. 이는 인간의 인지 과부하를 줄일 수 있습니다.

마무리

3주 정도의 시간을 가지고 즐겁게 읽었던 책이였다는 느낌을 받았습니다.

그리고 거대하고 느껴지던 객체지향 이라는 패러다임을 간결하게 정리할 수 있었던 시간이었습니다.

물론, 기본 개념이 간결한 것이지 실무에서 적용하기 위해서는 다양한 기법을 알아야 할 텐데요.

원리에 기반한 기법의 습득은 이해도의 폭을 넓히고 활용도의 폭도 넓혀 준다고 믿기 때문에 천천히 습득해가면 된다고 생각합니다.

객체지향이 부담스럽게 느껴지시는 분들에게 이 책을 추천합니다.

샘플 코드

Git 저장소에 앨리스 모델링 예제를 공유합니다.

지난 기록