[책 공부] 루비로 배우는 객체지향 디자인

3 minute read

책 소개

루비로 배우는 객체지향 디자인

객체지향 OOP에 대해서 공부하고 싶다면 이 책을 추천합니다. 좀 더 실무적인 관점에서 공부할 수 있어 좋았고 어떤 코드가 유지보수하기 좋은지 어떻게 설계해야 하는지 예시 도메인을 제시하고 설명해나갑니다.

디자인은 어떻게 실패하는가

디자인이 없는 애플리케이션은 그 자체로 붕괴의 씨앗을 품고 있다. 이런 애플리케이션은 쉽게 작성할 수 있지만, 점점 더 수정할 수 없는 애플리케이션이 된다. 나중에는 프로그래머가 새로운 수정 사항이 들어올 때마다 이렇게 말하기 시작한다. “응, 그 기능 추가할 수 있어. 대신 다른 기능은 다 고장날거야.”

변화를 손쉽게 받아들일 수 있도록 코드를 배치하는 일, 이것이 디자인이다.

데이터(data)가 아닌 행동(behavior)에 기반한 코드를 작성하라

행동은 메서드 속에 담겨 있고 메시지를 보내는 행위를 통해 실행된다. 하나의 책임만 지는 클래스(SRP)를 만들면 각각의 작은 행동들은 단 한 곳에만 존재한다. “반복하지 말 것(Don’t Repeat Yourself, DRY)”. DRY한 코드는 변화를 잘 견뎌 내는데, 클래스의 행동을 수정하기 위해 코드의 오직 한 부분만 수정하면 되기 때문이다. 그리고 데이터 구조는 숨겨라. 데이터가 필요하다면 객체가 가지는 책임, 즉 행위을 만들어라. 데이터 구조를 들여다보던 작업을 객체에 대한 메시지를 전송으로 대체한다. 객체가 행동에 집중하게 되면 그 객체가 하는 일이 무엇인지 더욱 명확해진다.

  • 주석을 넣어야 할 필요가 없어진다. - 클린코드(Clean Code) 책에서도 나오는 말
  • 재사용을 유도한다.
  • 다른 클래스로 옮기기 쉽다. - 리팩토링

객체의 책임이 많다면 추가적인 책임들을 격리시킨다. 즉, 별도의 클래스를 만들어서 책임을 분리시키는 것이다. 이렇게 해도 복잡하지 않은 이유는 응집도가 높기 때문이다.

도메인

완전히 새로운 애플리케이션의 첫 코드를 작성하는건 두려운 일이다. 이미 있는 코드에 새로운 코드를 추가할 때는 기존 디자인을 따르지만 새 하얀 도화지에 애플리케이션의 패턴을 영원히 규정하게 될 결정을 내려야만 한다.

먼저 패스트핏의 사업모델을 보면서 이 애플리케이션에 들어갈 법한 클래스가 어떠 것들이 있는지 떠올른다. 자전거 여행 회사라면, Customer(여행객), Trip(여행), Route(여행길), Bike(자전거) 그리고 Mechanic(정비공) 클래스 정도를 떠올랐다. 이런 클래스들을 바로 떠올릴 수 있었던 이유는 이 클래스들이 애플리케이션 속의 명사들, 정보(data)와 행동(behavior) 둘 다를 가지고 있는 명사들을 표현하기 때문이다. 이것들을 도메인 객체(domain objects)라고 부른다.

전문가들은 도메인 객체가 아니라 도메인 객체들이 주고받는 메시지에 집중한다. 이 메시지들은 새로운 객체를 찾도록 도와주는 가이드다. 도메인 객체만큼이나 꼭 필요하지만 잘 드러나지 않는 새로운 객체를 찾도록 도와준다. 키보드를 잡고 타이핑을 시작하기 전에 우리의 유스케이스를 만족시켜 줄 수 있는 객체들 그리고 메시지들의 의도를 구상할 필요가 있다.

  • 시퀀스 다이어그램

객체들간의 협력 관계속에서 중요한 것은 어떻게(How)해야 하는지 말해주지 말고, 어떤 것(What)을 달라고 요구(요청)하는 것이다. 그러면 협력하는 객체가 응답해주는 것이다. 즉, Bike 클래스에서 계산하는 하는 로직을 Trip 클래스에서 대신 하지 말자는 것이다. 계산은 Bike가 하도록 시키고, 그 결과를 달라고 Trip 클래스에서는 요청만 하는 것이다. 그러면 Bike 클래스가 응답을 내려줄 것이다. 이렇게 해야 객체들간의 응집도가 높아지고 결합도는 낮아진다. 각 도메인 객체들이 독립적으로 자신의 역할과 책임을 다하는 것이 된다.

Trip 클래스와 Customer 클래스 간의 협력 관계에서 메시지에 주목하면 여행을 여행객과 어울리게 만드는 클래스인 TripFinder 클래스가 필요하게 된다. 즉, 객체가 아닌 메시지에 주목하면 클래스는 자동적으로 따라 나온다.

덕 타이핑

덕 타이핑(Duck Typing)은 동적 타입의 한 종류로 객체의 변수 및 집합이 객체의 타입을 결정하는 것을 말한다. 클래스 상속이나 인터페이스 구현으로 타입을 구분하는 대신, 덕 타이핑은 객체가 어떤 타입에 걸맞은 변수와 메소드를 지니면 객체를 해당 타입에 속하는 것으로 간주한다.

Duck의 뜻이 오리인데, 만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽦거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.

이렇게 보면 덕 타이핑은 유연하다고 볼 수 있지만, 동적 타입인 만큼 컴파일 타임에 체크하지 않기 때문에 런타임 에럭가 발생할 수 있고, 상속이나 인터페이스처럼 제약을 주는 것이 아니기 때문에 의도치 않은 에러를 발생시킬 수 있다.

그럼에도 덕 타이핑을 사용하는 것은 컴파일 타임에 일일히 체크하지 않고 타입도 코드 작성시 명시 않으므로 좀 더 빠르게 코드를 작성할 수 있다. 또한 이미 존재하는 외부 라이브러리나 서드파티 객체들을 통합할 때 유용하다.

정적 타입 언어인 자바에서는 덕 타이핑 대신에 어댑터 패턴을 사용하여 인터페이스 타입을 지원하는지 체크(supports)한 다음에 사용(handler) 가능하다.

상속

상속은 코드 재사용성 공통 기능을 부모 클래스에 한 번만 작성하면 여러 자식 클래스에서 재사용할 수 있다. 또한 부모 클래스에서 한 번만 수정하면 모든 자식 클래스가 반영된다. 사실 이것이 장점이 아닐 수 있다.

그 이유는 강한 결합 때문이다. 부모의 내부 구현에 의존되어 있기 때문에 자식 클래스가 독립적으로 책임을 갖기 어렵다. 그렇기 때문에 부모 클래스에서 수정한다고 자식 클래스에도 영향이 가는 것은 좋지 않은 설계이다. 자식 클래스들은 독립적으로 존재해야 한다. 이렇게 설계가 되어야 응집도가 높아지는 것이다. 상속을 사용한 설계는 결국 SOLID 원칙에 위배되기도 한다.

그래서 추상 클래스를 사용한다. 부모의 구현을 따라 쓰는 것이 아닌 자식 클래스에서 직접 구현하여 독립적으로 기능을 수행한다. 이것이 응집도를 높이는 추상 클래스의 장점이다. 더 이상 부모 클래스에 영향을 받지 않아도 된다는 의미이다.

테스트코드

테스트코드 작성의 의미

  • 나중에 애플리케이션은 점점 커질수록 버그를 만나기 쉬운데 테스트코드는 이러한 버그를 쉽게 찾아낼 수 있다.
  • 도메인 로직을 이해할 수 있다. 테스트코드 작성이 곧 문서화이다.
  • 자연스레 추상화 설계를 높일 수 있다.
  • 안전하게 리팩토링할 수 있다.
  • 자연스레 디자인 패턴, 아키텍처를 적용할 수 있다.

테스트는 꼭 필요하다. 잘 디자인된 애플리케이션은 매우 추상적이고, 계속 변경된다. 테스트가 없다면 이해할 수도 없고 안전하게 수정할 수도 없을 것이다. 최상의 테스트는 실제 코드와 느슨하게 결합되어 있어야 한다. 그리고 모든 코드를 한 번만, 제대로 된 장소에서 테스트해야 한다. 이런 테스트는 코드 작성 비용을 높이지 않으면서도 새로운 가치를 제공한다. 모든 상황에 적응할 수 있으며 예상치 못했던 그 어떤 요구사항에도 대처할 수 있다.

Comments