![[기술서적] 객체지향의 사실과 오해](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FcideIA%2FdJMb9P0HjKt%2FAAAAAAAAAAAAAAAAAAAAAGlyvqn2pCGQyXg-brFAB1z9iz3U1Gy-QyjBi0jSAbll%2Fimg.jpg%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1761922799%26allow_ip%3D%26allow_referer%3D%26signature%3Drf1wW5x66ipeRcrAG9yv7LCyv7Y%253D)
객체지향 설계를 다루는 '객체지향의 사실과 오해'라는 조영호님의 책을 읽고서 정리하고, 저의 생각에 대해서 작성해보았습니다.
한 챕터씩 읽어가면서 생각을 바로 정리하기 위해 챕터별로 나누었고, 개인적인 의견이 다소 섞여 있기 때문에 글의 옳고 그름은 있을 수 있습니다. 피드백 주실만한 부분이 보이신다면 말씀주시면 감사하겠습니다.
Chapter 1, 2
책의 도입 부분에서부터 내가 알고 있던 객체지향에 대한 오해를 말해주고 있다.
처음 배울 때 객체지향은 '현실세계의 사물을 모방해 소프트웨어로 구현하는 것'이라고 접했는데, 이러한 오해는 2장까지 읽고나서 대략적으로 이해를 할 수 있었다. 내가 이 책을 통해 이해한 내용은 아래와 같다.
객체지향의 목표는 실세계를 모방하는 것이 아닌 새로운 창조와 같다.
'클래스'와 '객체'라는 개념적 용어가 나오면서 클래스 지향이 아닌 객체가 중심이 된다고 설명하고 있는데, 사실 이 두개는 같은 것이라고 생각하고 있다. 왜냐하면 "클래스라는 정의를 기반으로 객체라는 인스턴스를 생성하여 개발을 진행해왔지 않았던가."
왜 다르게 설명하는지 궁금하여 계속 생각해보았는데, 책에서는 객체 각각의 상태와 행동을 설명하고 있고 이러한 객체들 사이의 협력 관계를 제대로 설계하는것이 중요하다는것을 언급하기 위해 이처럼 설명한 것 같았다.
즉 협력을 직접적으로 수행하는 객체의 관점에서 클래스를 설계해야함을 의미하는 것 같다.
또한 책에서는 방금 언급했던 '상태', '행동', '협력'을 실제 세계와 동화를 통해 비유적으로 설명하고 있다. 덕분에 쉽게 이해할 수 있었고, 정리해보면 다음과 같다.
소프트웨어에서는 어떠한 것도 객체가 될 수 있고, 각각의 객체는 '자율성'을 가지고 있다.
이러한 객체 내부에는 상태를 나타내는 값(프로퍼티)들이 존재하고, 행동(메서드)을 통해 객체간의 상태를 변경시킬 수 있다. 상태는 외부(다른 객체)로부터 감춰져야 하고, 행위를 통해서만 협력(상태 변경)을 수행할 수 있어야 한다고 설명한다.
협력은 '메시지'를 통해 이루어지고, 메시지를 수신한 객체는 자율성을 가지고 자기자신의 상태를 변경시킬 수 있다.
책에서는 객체를 설계할 때 '행동'이 '상태'를 결정한다고 말하며, 행동을 중심으로 설계를 진행하라고 말한다. 2장에서는 이해가 어려웠는데, 생각해보면 행동이 협력의 기반이 되고, 협력을 통해 상태를 변경시키는 보이지않는 순서에 의한 문장이라고 이해하고 있다.
2장의 마지막에서는 '모방'은 아니라 '은유'라는 표현으로 실제 세계의 사물에 대해 어느정도의 은유를 통한 객체를 설계할 수는 있다고 말하고 있다. 나 또한 객체를 설계할 때 어떤 이름으로 지어야 가독성이 좋을까에 대해 고민하는데 보통은 현실 세계의 사물 중 비슷한 것을 골라 네이밍 또는 상태를 정의하고 있었다. 그래서 그런지 곧바로 이해를 할 수 있었던 것 같다.
Chapter 3.
3장에 들어서는 '타입'과 '추상화'에 대한 주요 개념들을 설명해준다. 인상깊었던 점은 지하철 노선도의 역사를 비유로 추상화에 대해 설명해주고 있는데, 이 부분이 놀랍도록 직관적으로 와닿았다는 점이였다.
추상화란 기존 정보의 복잡성을 간단하게 나타내기 위해 사용된다. 여기서는 두 차원에서 이루어진다고 말해주는데, 간단히 정리해보면 공통점과 중요한 부분만을 드러낸다는 것이다. 이 과정에는 추상화를 하는 '목적'에도 주의깊게 고려하며 공통점과 중요한 부분을 추려내야할 것이다.
또한 추상화의 결과물은 '개념'이 되어 특정한 객체 집합을 나타내는 데 사용될 수 있다. 이러한 추상화의 결과물(개념)은 다양한 객체를 논리적으로 '분류'할 수 있도록 도와주는데, 분류된 객체는 해당 개념의 '인스턴스'가 된다.
분류를 위한 방법은 '심볼', '내연', '외연' 3가지 관점에서 할 수 있는데, 사실 '내연'에 의해서 분류를 할 수 있다고 생각된다. 중요한 점은 그 객체가 어떤 행동을 할 수 있는가에 있다.
또한 추상화의 결과물인 개념은 타입이라고도 할 수 있다고 말해주고 있다. 타입의 예시로 데이터 타입을 빗대어 설명하고 있는데, 간단하게 특정 데이터가 어떤 행동(산술 연산, 논리 연산 등)을 할 수 있는지에 따라 타입을 분류할 수 있다는 말을 하고 있는것 같았다.
중요하게 생각해두어야 할 것은 '행동'에 따라서 객체를 분류하고, 타입을 분류해야 한다는 것이다. 또한 행동에 의한 결과는 철저하게 감춰져야 한다.
이러한 타입에는 계층이 존재하는데, 계층을 구분하는 기준에는 일반화/특수화 관계가 존재한다.
일반화/특수화 관계 또한 객체가 어떤 '행동'을 하느냐에 집중하고 있는데, 일반화의 '행동'을 모두 가지고 있는 상태에서 특수한 '행동'을 추가로 가지고 있는 객체의 경우 서브타입(Subtype)이 되고, 모든 객체들의 공통적인 '행동'만 가진 경우를 슈퍼타입(Supertype)이 된다. 이 관계에서는 상속 관계 또한 적용된다. (슈퍼타입 -> 서브타입)
조금 정리를 해보자면, 추상화 기법에는 두가지 중요한 사항이 있다.
- 객체들의 공통점을 기반으로 분류
- 객체들의 불필요한 특성을 제거하고 중요한 특성만 가지고 일반화
사실 2번의 경우에는 특수화된 타입에서 일반화 타입을 고려할 때 사용된다.
3장을 읽으면서 여기에서 말하는 개념, 타입 들을 클래스라고 생각했었는데, 클래스는 개념을 나타내기 위한 방법 중 하나로 둘을 일치시키면 안된다고 마지막에 해소시켜 주고있었다.
Chapter 4.
내가 생각한 4장에서 전달하고자 하는 핵심 내용은 다음과 같다.
"객체지향에 대한 흔한 오류는 데이터와 클래스를 중점으로 보는것이다. 제대로 된 객체지향 설계를 하기 위해서는 '협력'이라는 관점의 변화가 필요하다."
또한 위에 나오는 역할과 책임 그리고 협력에 대해서 윤곽을 잡을 수 있었다.
협력이란 요구사항에 대한 추상적인 양상을 나타낸다. 책의 예로는 재판이라는 요구사항이 이루어지기 위해 판사, 증인이라는 역할이 필요한 것처럼 어떠한 협력관계가 이루어지는지 찾고 설계하는것이 중요하다.
책임은 객체가 어떤 요청에 대해 응답을 해줄 의무가 있을 경우 해당 객체가 어떠한 책임을 가진다고 말할 수 있다.
이러한 책임은 '하는 것'과 '아는 것' 두 가지로 분류할 수 있는데, 하는 것의 경우 말그대로 객체를 다루는 행동을 의미하고 아는 것의 경우 해당 객체만이 가지는 정보라고 말할 수
객체에게 적절한 책임을 분배하는 것이 설계에서 중요하다고 한다. 이 경우에는 해당 책임을 수행할 수 있는 객체가 여러 존재할 경우 필요한 부분이라고 생각한다.
역할은 위에서 언급한 판사와 증인과 같은 책임의 추상화 결과물이다. 각 객체는 특수하지만 공통점 또한 존재한다. 나는 이런 공통점을 묶어서 추상화하는 과정이 '역할'을 만드는 과정이라고 생각한다. 이 역할을 제대로 나타낼 경우 수많은 협력 관계를 하나의 역할로 단순화시킬 수 있는 장점이 있어 보인다. (내게는 교집합을 찾는 과정으로 보였다.)
읽는 과정에서는 위와 같이 생각을 했었는데, 이 부분은 조금 잘못된게 있다.
어떤 협력이 있어야 하는지 정의 -> 협력관계에서 각각의 역할을 탐색 -> 역할이 수행해야 할 책임을 정의 -> 정의된 책임을 수행할 수 있는 객체에게 책임을 할당하는 과정이 좋은 설계의 흐름이지만, 위에서는 객체에서부터 설계가 파생이 되는것처럼 생각했었다. (오랜 습관 때문인지..)
추가로 객체지향의 설계 기법으로 '책임-주도 설계', '디자인 패턴', '테스트-주도 개발' 3가지를 말해주고 있다.
테스트-주도 개발이 한때 인기가 엄청 있었던 만큼, 나 또한 시도해본적이 있었는데 결국에는 테스트 코드 작성은 흐지부지 되었던 기억이 있다.
책을 통해 그 이유에 대해서 알 수 있었는데, 책임-주도 설계에 대한 개념이 부족한 상태에서 아무 목적의식도 없는 테스트 코드를 작성하고 있었기 때문이였던 것 같다. 테스트-주도 개발에서 테스트 코드의 목적은 책임과 역할 사이 협력 관계가 정상적으로 이루어지는지를 보기 위함이다. 그렇기에 객체지향 설계에 대한 깊은 이해가 바탕이 되어야 비로소 빛을 볼 수 있는 개발 방법이기에 나는 제대로 적용하지 못했던 것 같다.
Chapter 5.
'자율적인 객체'란 어떤 것인가, 객체가 자율적인 책임을 가지게 될 경우 어떤 강력함이 생기게 되는지 알 수 있었다.
객체의 자율성은 적절한 '책임'을 정의하는것에서부터 시작된다. 어떤 책임을 가지냐에 따라서 객체의 행동반경이 제한되기 때문이다.
구체적인 책임은 객체의 자율성을 흐리게 만들고, 너무 추상적인 책임은 명확하지 않은 협력을 만들게 된다. 이러한 애매모호함 사이에 자율적인 책임을 판단할 수 있는 특징이 존재하는데, 객체가 '어떻게'가 아닌 '무엇'을 해야 하는지를 나타내는것이다.
결국은 어떤 응답을 기대하는지에 대해서 생각하는게 중요하며, 그 응답이 오는 과정에 대해서는 구체적으로 제한하면 안된다는 것이다.
이러한 책임은 '메시지'라는 형태로 객체에게 할당이 되고, 객체는 '메서드'를 통해 이를 수행하게 된다. 즉 메시지로 '무엇'을 해야하는지를 전달하고, 객체는 '어떻게'를 메서드를 통해 자율적으로 수행하는게 이상적이다.
책임을 수행할 수만 있다면 어떤 객체에게 할당이 되도 상관이 없으며, 각 객체는 자신들의 메서드를 통해 자율적으로 책임을 수행한다. 여기서 중요한 점은 어떤 객체에게 할당이 됐는지에 대해 메시지를 전달한 송신측에서는 알 필요가 없다는 것이다.
이를 '다형성'이라 부르며, 유연하고 재사용 가능한 설계를 가능하게 하는 중요한 특성 중 하나다.
객체는 수신할 수 있는 메시지(수행할 수 있는 책임)를 가진다. 책임을 수행하는 구체적인 방법인 메서드 또한 가지고 있다.
여기서 하나하나의 메시지는 '인터페이스'의 구성 요소가 되며, 외부로 노출시켜 협력의 시작점 역할을 수행한다.
메서드는 '캡슐화'에 의해 내부로 감춰지는데, 이는 객체의 자율성을 지킴과 동시에 변경이 외부에 영향을 가지않게 하는 특성이 된다.
책임(메시지 또는 인터페이스)을 자율적으로 만드는 것은 협력 관계를 쉽게 이해할 수 있게 하며, 구현의 자율성이 캡슐화를 도움으로써 설계가 유연해지며, 이러한 객체지향의 장점을 누릴 수 있는 시작점이 된다.
Chapter 6.
객체지향 설계에 대한 실질적인 힌트를 많이 얻을 수 있었다.
먼저 설계란 변경에 안정적으로 대비하기 위해 필요한 과정이다. 소프트웨어 또한 요구사항에 의한 변경이 자주 일어나며, 이러한 변경에 대비하기 위한 설계는 매우 중요하다. 불안정한 설계는 변경에 필요한 비용을 크게 늘리기 때문이다.
이 책의 설계에서는 '기능'과 '구조' 두 가지 측면에서 모든걸 설명해주고 있다. 사용자에게 제공될 기능을 마련하기 위해 구조를 만들게 되는데, 각각 '유스케이스'와 '도메인 모델'을 통해 접근할 수 있다.
유스케이스와 도메인 모델은 바라보고 있는 것에 대해 추상화하여 단순하게 표현하는 것이라고 생각된다. 이를 '멘탈 모델'이라고 표현할 수도 있는 것 같은데, 멘탈 모델은 디자인, 사용자, 시스템 세 가지 측면에 의해 만들어진다고 한다.
도메인의 경우 사용자들이 바라보는 개념을 의미하며, 이러한 개념은 쉽게 바뀌지 않는 특성을 가지고 있다. 이러한 도메인을 모델링하여 소프트웨어로 녹여냈을 때, 변경에 쉽게 대비할 가능성이 커지게 된다.
이러한 도메인 모델은 소프트웨어의 안정적인 구조(객체 구조)를 제공할 수 있다.
도메인 모델을 통해 구조화된 객체에게 유스케이스를 활용한 책임을 분배하는 것으로 안정적인 소프트웨어를 만들 수 있다.
유스케이스의 핵심은 사용자와 시스템 간의 상호작용의 흐름을 나타내는 것이다. 이러한 흐름은 여러가지 시나리오로 이어질 수 있으며, 사용자가 목표를 달성하기 위해 시스템과 어떤 커뮤니케이션을 해야하는지에 대해서 나열하면 된다.
도메인 모델과 같은 쉽게 변하지 않는 구조를 먼저 설계에 적용하고, 유스케이스에 정리된 기능들을 도메인 객체들의 책임으로 할당하는 과정을 통해 사용자와 설계자의 거리감을 줄일 수 있다. 사용자가 바라보는 도메인 관점을 소프트웨어 구조에 반영함으로써, 변경에 안정적이면서도 사용자의 요구를 충족하는 시스템을 만들 수 있다.
Chapter 7.
마지막으로 객체지향 설계를 바라보는 세 가지 원칙을 설명해주고 있다.
각각 개념 관점, 명세 관점, 구현 관점으로 도메인/인터페이스/구현으로 분리할 수 있을 것 같다.
개념 관점의 경우 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현하고 있다. 사용자가 바라보는 도메인을 이해하고, 이해한 도메인을 최대한 설계에 녹여냄으로써 관리하기 쉽고 유지보수성이 향상된 소프트웨어를 만들 수 있다고 말하고 있다.
명세 관점의 경우 객체들의 책임에 초점을 맞추고 있다. 어렴풋이 나온 도메인 모델 내에서 어떤 협력 관계가 존재하는지 찾는 과정에 메시지가 보이며, 이러한 메시지들을 변화에 탄력적으로 만들 수 있도록 권장하고 있다.
구현 관점의 경우 매우 간단하다. 명세 관점에서 발견한 책임을 어떻게 수행할 것인지에 초점을 맞추는 실제로 구현하는 코드라고 볼 수 있다.
명세 관점의 인터페이스의 경우 변화가 발생할 경우 협력 관계의 모든 객체에게 영향이 파생된다. 이는 곧 설계의 흔들림을 의미하기에 무엇보다 변화에 흔들리지 않도록 만들 수 있어야 한다고 한다.
강한 인터페이스는 구현부의 세부 사항을 감추는 것으로부터 시작된다고 볼 수 있을 것 같다.
+ 추상화
부록 '추상화 기법' 부분은 읽으면서 저자가 어떤걸 전달하고자 하는지 제대로 이해하기 힘들었다.
반복해서 읽게 되었고, 아래는 내가 정리해본 생각들이니 참고하면 좋을 것 같다.
도메인 모델을 실제 코드로 옮기기 위한 추상화를 하는 작업에는 세 가지 기법을 적용할 수 있다.
- 분류와 인스턴스화
- 일반화와 특수화
- 집합과 분해
3가지 모두 복잡성을 줄이는 것에 목표를 두고 있다.
'음료'라는 도메인은 콜라, 사이다, 환타 등등의 개별 사례들이 존재한다.
이러한 개별 부분에 대해 '탄산'이라는 공통점을 추출해서 클래스를 구상한다면 1번에 해당한다.
세 가지 음료는 '마실 것'이라는 일반적인 개념이 있다.이러한 공통되는 점은 상위로, 색깔과 같은 차이점은 하위로 계층 구조 형식으로 나타낸다면 2번이 될 것이다.
음료는 담을 용기, 성분 등등의 음료를 구성하는 부분들이 있다. 이렇게 도메인이라는 전체에서부터 이를 구성하는 작은 부분들로 분해하면서 추상화가 진행된다면 3번에 해당할 것이다.
위 기법들을 구현하기 위한 도구로 클래스, 상속, 합성, 집합, 패키지 등의 구현 방법들을 적용할 수 있다.
마지막으로 이번 서적을 읽고 얻은 부분을 간단명료하게 정리하자면 다음과 같다.
"좋은 코드에 대한 기준점을 얻었다."
지금껏 개발하면서 배웠던 것들, 참고했던 것들이 좋은 코드라 생각하고 그대로 따라하기 위해 힘썼다. 그렇기에 프로젝트가 끝나고 다음 개발이 시작될 때, 앞서 진행했던 컨벤션을 토대로 늘 같은 틀에서 개발을 진행했었고 코드 리뷰에도 뚜렷한 주관을 가지지 못했던 것 같다.
그래서 추석 연휴를 빌미로 기초부터 다시 시작하자라는 마음에 이번 서적을 읽게 되었고, 생각보다 좋은 가독성과 공감가는 내용들, 무엇보다 좋은 예시들이 있어서 객체지향에 대한 기반을 만들 수 있었던 것 같다.
마지막으로 엘리스 동화를 읽었던 적이 없지만 정주행한 것 같은 느낌이 듭니다...
'Language > Java' 카테고리의 다른 글
[Java] 부동소수점 오차를 피하자! (0) | 2025.03.11 |
---|---|
[Java] String, StringBuilder, StringBuffer 비교 (0) | 2025.03.09 |
자바 표준 라이브러리 (지속 업데이트) (0) | 2024.11.28 |
[Java] 객체 동등 비교 - equlas() (0) | 2024.10.05 |
[Java] 인스턴스 변수를 사용하지 않는 메서드는 static을. (0) | 2024.09.07 |
개발 기술 블로그, Dev
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!