⌜오브젝트⌟가 쓰여진 이유
프로그래밍 패러다임은 개발자 공동체가 동일한 프로그래밍 스타일과 모델을 공유할 수 있게 함으로써 불필요한 부분에 대한 의견 충돌을 방지한다. 또한 프로그래밍 패러다임을 교육시킴으로써 동일한 규칙과 방법을 공유하는 개발자로 성장할 수 있도록 준비시킬 수 있다. 이 책은 객체지향 패러다임에 대한 책이다.
이 책의 목적은 객체지향 패러다임이 제시하는 프로그래밍 패러다임을 설명하는 것이다. 객체지향 패러다임이라는 용어를 사용할 때, 어느정도 유사한 그림을 머릿속에 그릴 수 있는 기반을 제공할 것이며, 객체지향에 대한 오해를 제거하고, 객체지향 프로그래밍을 하는 개발자들이 동일한 규칙과 표준에 따라 프로그램을 작성할 수 있게 할 것이다.
도서를 학습하며
하루에 약 6~8시간 꼬박 일주일에 걸쳐 오브젝트라는 도서를 완독했다.
오브젝트라는 도서는 객체지향이 무엇인지, 객체지향적인 설계를 하기위한 접근방법 등 여러 시점에서 객체지향에 대해 설명한다. 가장 인상 깊었던 내용은 객체를 데이터의 집합이라고 생각하기 보다는 문제 해결을 위한 협력관계 속에서 어떠한 역할을 가지고 책임을 수행하는 주체로 보라는 것이다. 해당 내용은 설계를 진행할 시 유연한 설계에 도움이 될 것이라고 생각한다.
지금까지 나는 객체지향을 학습하며 진행했던 여러 자바 프로젝트에서 객체를 데이터 관점에서 먼저 바라봤던 것 같다. '객체가 어떤 행위를 수행하는 가' 보다는 '해당 객체는 어떤 데이터를 가져야 하는가?'를 먼저 생각한 뒤, '해당 데이터를 가지고 있는 객체는 어떤 행위를 수행해야 하는가'로 접근했다. 해당 접근 방식의 설계는 처음에는 그럴듯 한 객체 분리가 이루어졌으나, 개발을 진행하면 할 수록 외부에 공개해야 하는 데이터가 늘어나고, 응집도가 떨어지는 듯한 느낌을 주었다.
또, 나는 지금까지 객체지향 맹신론자였다. 은연중에 '객체지향적인 설계가 무조건 좋아.'라는 생각을 가지고 있었던 것 같다. 하지만 실상은 그렇지 않았다. 모든 기술은 트레이드 오프의 산물이며, 은탄환은 없다. 이 말을 항상 머리속에 넣어두려 한다.
"객체지향이 적합하지 않은 상황에서는 언제라도 다른 패러다임을 적용할 수 있는 시야를 기르고 지식을 갈고 닦아야 한다."
해당 도서를 통해 설계 접근 방식에 대해 알게되었고, 나의 문제점 또한 파악할 수 있었다. 이제 내가 학습하며 습득했던 지식을 나중에 다시 볼 수 있도록 간단하게 정리하고 다음 학습 단계로 넘어가려 한다.
객체지향이 필요한 이유
우리가 프로그래밍을 진행하며 항상 마주하는 것이 있다. 바로 변경이다. 도서에서는 이렇게 말한다.
우리는 프로그램을 개발하며 두 가지 요구사항을 만족시켜야 한다. 기능을 수행하는 코드를 구현해야 하며, 동시에 내일 쉽게 변경할 수 있는 코드를 짜야 한다. 좋은 설계란 오늘 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계다.
우리는 오늘의 요구사항을 만족하는 기능을 개발함과 동시에 내일의 변경을 매끄럽게 수용할 수 있는 애플리케이션을 개발해야 한다. 애플리케이션에 있어 변경은 필수 불가결한 존재이다. 변경을 고려하지 않고 애플리케이션을 설계하고 개발한다면, 시간이 흐름에 따라 서서히 변경에 있어 드는 비용이 증가할 것이고, 어느 순간부터 변경이 두려워 질 것이다. 나도 잘못된 설계로 인해 이러한 경험을 겪었고, 해결해본 적이 있다.
변경을 유연하게 하기 위해서는 그것이 객체가 됐든, 모듈이 됐든, 컴포넌트가 됐든 각각 강한 응집도를 가지고 있어야 하며, 느슨한 결합을 통해 연결되어 있어야 한다. 애플리케이션이 낮은 응집도를 가진다면 동일한 시점에 동일한 이유로 변경되어야 하는 코드가 광범위하게 퍼져있을 것이고, 강합 결합도를 가진다면 여러 객체가 한번에 변경되어야 할 것이다.
그렇다면 객체지향적인 설계가 무조건 좋을까?
결론부터 말하자면 그렇지 않다. 객체지향적인 설계는 확실히 객체 간 느슨한 결합과 강한 응집도를 통해 변경을 유연하게 만들어 준다. 도서에서는 설계에 대해 '설계는 변경의 용이성을 위해 존재한다.' 라고 말한다. 아쉽게도 객체지향적인 설계는 변경의 용이성을 취하는 반면에 코드의 복잡도를 높인다.
설계는 트레이드오프의 산물이다. 느슨한 결합을 위해 최소한의 의존성으로 설계를 진행한다면 당연히 코드의 복잡도는 올라갈 것이다. 반대로 강한 결합을 통해 설계한다면 코드의 복잡도는 낮아질 것이다. 오브젝트에서는 이렇게 말한다. 설계는 변경과 유지보수를 위해 존재한다는 점을 기억해야 한다. 대부분의 경우에는 단순한 설계가 정답이지만, 변경에 따르는 고통이 복잡성으로 인한 혼란을 넘어서고 있다면 유연성의 손을 들어주는 것이 현명한 판단일 확률이 높다.
과연 몇년에 한 번 주기로 변경될 가능성이 있는 기능을 객체지향적으로 설계하는 것이 정말 좋을까? 이렇게 변경될 가능성이 거의 존재하지 않는 기능은 절차지향대로 개발하며 가독성을 취하는 것이 좋다고 생각한다.
객체지향적인 설계 방법
객체지향적인 설계란 애플리케이션이 해결해야 하는 문제영역을 여러 역할과 책임의 협력관계로 바라보는 것으로 시작한다. 데이터와 객체를 먼저 생각하기 보단, 문제를 해결하는 데 있어 어떤 책임이 필요한지 파악하고, 유사한 책임을 하나의 역할로 추상화하며, 이를 협력관계로 묶어 생각하는 것이 좋다.
도서에서는 객체지향적인 설계를 위해 책임 주도 설계(Responsibility-Driven Design)를 따르는 것을 추천한다. 협력을 설계하기위해서 책임에 초점을 맞춰야 한다. 어떤 책임을 선택하느냐가 전체적인 설계의 방향과 흐름을 결정한다.
도서에서 설명하는 책임 주도 설계의 순서는 아래와 같다.
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.
- 시스템 책임을 더 작은 책임으로 분할한다.
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.
- 객체가 책임을 수행하는 도중 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 된다.
내가 과거의 나에게 해주고 싶은 말이 있다면, 데이터와 객체를 먼저 구성하는 것이 아닌, 메시지를 먼저 구상하라는 것이다. 메시지를 먼저 구상하고 메시지를 처리하기 위한 데이터를 객체에게 할당함으로써, 우리는 퍼블릭 인터페이스 중심의 설계를 진행할 수 있다. 이러한 접근 방식으로 객체를 설계한다면, 객체는 최소한의 퍼블릭 인터페이스를 가질 수 있으며, 나머지 정보는 캡슐화하는 것이 자연스럽게 진행된다.
상속에 대한 오해
상속(extends를 통한 is-a 관계)는 런타임 다형성을 성립하게 해주는 고마운 기능이다. 하지만 나를 포함하여 여러 개발자가 이를 오용하고 있다는 것을 도서를 통해 깨달았다. 문제는 상속을 다형성이 아닌 코드의 재사용 목적으로 바라보는 것이다.
상속을 코드의 재사용 목적으로 사용하는 것을 도서에서는 서브클래싱(subclassing) 이라고 칭한다. 서브클래싱은 수퍼클래스와 서브클래스 간의 강한 결합도를 가지게 한다. 왜냐하면 서브클래스 입장에서는 수퍼클래스를 자연스럽게 화이트박스로 바라봐야 하기 때문이다. 서브클래스는 수퍼클래스의 내부 로직과 상태를 세세하게 알고 있어야 하며, 이는 곧 강한 결합도를 가지게 한다.
서브클래싱을 통해 문제가된 사례는 자바의 내부 라이브러리인 Vector와 Stack을 통해 확인할 수 있다. Stack은 Vector의 서브클래스이며, Vector의 코드를 재사용하기 위해 상속한다. 여기서 Vector가 가지고 있는 add 메서드를 사용하면, Stack의 가장 첫번째 요소에 값을 추가하는 것이 가능해져 버린다. 자세한 내용은 생략한다.
서브타이핑
느슨한 결합을 위해 우리는 상속을 코드의 재사용 목적 보다는 타입을 계층화한다는 목적으로 바라봐야 한다. 도서에서는 이러한 개념은 서브타이핑(subtyping)이라고 설명한다. 이는 부모 타입의 코드를 재사용하는 것이 아닌 퍼블릭 인터페이스의 호환성을 유지하며, 더 많은 기능을 추가할 수 있는 것을 의미한다. 즉, 클라이언트 입장에서 수퍼 클래스를 자식 클래스가 완벽하게 대체가능해야 한다는 것이다.
마치며
오브젝트는 객체지향에 대해 다시 생각해보고, 트레이드 오프를 생각해볼 기회를 준 도서이다. 해당 도서를 다시 학습하며 조금 더 상황에 맞는 설계를 할 수 있는 개발자가 되고 싶다.
객체지향 설계의 핵심은 역할, 책임, 협력이다. 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가지는 애플리케이션을 개발하는 것. 설계는 변경을 위해 존재한다. 변경은 어떤 식으로든 비용이 발생한다. 훌륭한 설계란 합리적인 비용안에서 변경을 수용할 수 있는 구조를 만드는 것이다.
객체를 재사용함에 있어 객체를 화이트박스로 바라보는 일을 줄여라. 객체를 화이트박스로 바라본다는 것은 객체 내부의 로직을 세세하게 알아야 한다는 의미이고 이것은 곧 두 객체의 강한 결합을 의미한다. 객체를 블랙박스로 바라보고 퍼블릭 인터페이스를 통해 협력한다면 두 객체 간의 결합을 느슨하게 가져갈 수 있고, 이는 곧 변경의 유연함으로 이어진다.
설계는 트레이드오프의 산물이다. 느슨한 결합을 위해 최소한의 의존성으로 설계를 진행한다면 당연히 코드의 복잡도는 올라갈 것이다. 반대로 강한 결합을 통해 설계한다면 코드의 복잡도는 낮아질 것이다. 오브젝트에서는 이렇게 말한다. 설계는 변경과 유지보수를 위해 존재한다는 점을 기억해야 한다. 대부분의 경우에는 단순한 설계가 정답이지만, 변경에 따르는 고통이 복잡성으로 인한 혼란을 넘어서고 있다면 유연성의 손을 들어주는 것이 현명한 판단일 확률이 높다.
'일기' 카테고리의 다른 글
8주간의 휴식 기간을 가지며 (6) | 2024.07.13 |
---|---|
객체지향이란 무엇일까? (1) | 2024.04.18 |
백엔드 취준생이 클린 아키텍처를 읽고 (0) | 2024.02.14 |
우테코 프리코스 회고와 앞으로의 계획 (0) | 2023.12.13 |
내가 얻은 것들 그리고 얻어갈 것들 (1) | 2023.10.12 |