021. (Objects) 14. 일관성 있는 협력

14. 일관성 있는 협력

거듭 강조했듯 객체는 협력을 위해 존재하며, 협력은 객체가 존재하는 이유와 문맥을 제공한다.

객체지향 설계의 목표는 적절한 책임을 수행하는 객체들의 협력을 기반으로 결합도가 낮고 재사용이 가능한 코드 구조를 창조하는 것이다.

결국 객체지향 패러다임의 장점은 설계를 재사용할 수 있다는 것이며, 재사용을 위해 객체들의 협력 방식을 일관성있게 만들 필요가 있다.

일관성은 설계에 드는 비용을 감소시키며, 과거의 해결 방법을 반복적으로 사용해서 유사한 기능을 구현하는 데 드는 리소스를 대폭 줄일 수 있게 해준다.

무엇보다 일관성있는 설계가 가져다주는 장점은 코드가 이해하기 쉬워진다는 것이다.

따라서 일관성있는 협력 패턴을 적용하여 이해하기 쉽고 직관적이며 유연한 코드를 작성할 수 있도록 하자.

반대로 일관성이 없는 경우엔 어떻게 될까?

일반적으로 일관성이 없는 경우 두 가지 케이스에서 불편함이 발생한다.

첫 번째는 새로운 구현을 추가해야하는 상황 이고, 두 번째는 기존의 구현을 이해해야 하는 상황 이다.

위 두 상황을 전부 이해하고 진행해야하는 건 개발자이고, 대부분의 업무가 위 상황에 놓일 것이기때문에 지속적으로 더 높은 리소스를 요구하게 될 것이다.

따라서 유사한 기능은 유사한 방식으로 구현하여 일관성을 유지하는 중요하다.

만약 특정 기능에 대해 여러가지 방식으로 구현되어있는 경우, 우리는 매번 요구사항을 해결하기 위한 최적의 방법을 찾도록 강요받게 되며 이는 리소스 투입의 증가를 야기한다.

14.1. 일관성있는 설계 만들기

일관성 있는 설계를 만드는 데 가장 좋은 방법은 다양한 설계를 경험해보는 것이다.

다양한 설계 경험을 통해 어떤 변경이 중요한지, 어떻게 변경을 다뤄야하는지를 판단할 수 있는 직관을 키워야한다.

이 직관을 빠르게 습득하기 위한 방법 중 하나가 줄이기 위해선 널리 알려진 디자인 패턴을 학습하고 변경이라는 문맥 안에서 학습한 패턴을 적용해보는 것이다.

다만, 디자인 패턴을 통해 반복적으로 적용할 수 있는 설계 구조를 제공한다고 하더라도 모든 케이스에 딱 맞는 디자인 패턴이 있는 것은 아니다.

따라서 아래의 원칙에 지키면서 일관성있는 협력을 만들도록 해보자.

  • 변하는 개념을 변하지 않는 개념으로부터 분리한다.
  • 변하는 개념은 캡슐화한다.

대부분의 객체지향의 원칙과 개념들 역시 변경의 캡슐화를 목표로 한다.

새로운 요구사항을 접했을 때, 코드에서 바뀌는 부분이 있는지 검증하고 바뀌지않는 부분으로부터 분리하는 것이 첫 번째 설계 원칙이다.

14.2. 캡슐화 다시 보기

우리는 여전히 캡슐화하면 데이터 은닉을 떠올린다.

데이터 은닉이란 외부에 공개된 메서드를 통해서만 객체의 내부에 접근할 수 있게 제한하여, 객체 내부의 상태 구현을 숨기는 기법을 의미한다.

그럼 캡슐화는 데이터 은닉과 동치일까?

아니다. 캡슐화는 데이터 은닉 이상의 의미를 내포하고 있다.

진정한 의미의 캡슐화는 소프트웨어 안에서 변할 수 있는 모든 개념을 감추는 것을 말하며, 개념 안에 객체 내부의 상태가 포함될 뿐이다.

우리는 설계에서 무엇이 변화할 가능성이 있는지 고려하고, 재설계없이 변경할 수 있는 것이 무엇인지 고려해서 변화하는 개념을 캡슐화해야한다.

이 캡슐화를 종류에 따라 분류해보자.

  • 데이터 캡슐화 : 클래스 내부에서 관리하는 데이터를 캡슐화한다.
  • 메서드 캡슐화 : 클래스의 내부 행동을 캡슐화한다.
  • 객체 캡슐화 : 객체와 객체 사이의 관계를 캡슐화한다. (합성 관계가 바로 객체 캡슐화이다.)
  • 서브타입 캡슐화 : 서브타입의 종류를 캡슐화한다. (서브타입 캡슐화가 바로 다형성의 기반이 된다.)

위와 같이 분류했듯이 캡슐화는 단순히 데이터 은닉만을 의미하는 것이 아니라, 코드 수정으로 인한 파급 효과를 제어할 수 있는 모든 기법을 의미한다.

다소 생소할 수 있는 개념은 객체 캡슐화와 서브타입 캡슐화를 좀 더 명세해보면 아래와 같다.

서브타입 캡슐화

먼저 변하지않는 부분으로부터 변하는 부분을 분리하여, 변하는 부분들의 공통적인 행동을 추상 클래스나 인터페이스로 추상화한다.

이후 변하는 부분들이 이 추상 클래스나 인터페이스를 상속받게 만들어 변하는 부분을 변하지않는 부분의 서브타입 계층 으로 만든다.

객체 캡슐화

서브타입 캡슐화를 통해 획득한 타입 계층을 변하지않는 부분에 합성한다.

단 변하지 않는 부분에서는 변경되는 구체적인 사항에 결합되서는 안되며, 의존성 주입을 통해 느슨한 결합도를 유지해야 한다.

이제 변하지않는 부분은 변하는 부분의 구체적인 종류에 대해서는 알지 못하게 된다.

14.1. 일관성있는 설계 만들기

일관성 있는 설계를 만들기 위한 절차를 알아보자.

  • 1. 변경 분리
    • 일관성 있는 협력을 만들기 위해서 변하는 개념과 변하지 않는 개념을 분리한다.
  • 2. 변경 캡슐화
    • 변경을 캡슐화하여 파급효과를 줄인다.
    • 변하는 부분을 분리하여 공통 분모를 추상화하여, 이 충사화를 구현하도록 한다.
  • 3. 협력 패턴 설계
    • 변경의 캡슐화 이후, 변하지 않는 부분만을 이용해 객체 사이의 협력 방식을 겁토한다.
    • 추상화로 구성한 협력한 추상화를 구체적인 사례로 대체하여 확장이 가능해진다.
    • 이를 통해 재사용 가능한 헙력의 패턴이 명확하게 드러난다.
  • 4. 추상화 수준에서의 협력 패턴 설계
    • 캡슐화를 완료한 코드는 변하지 않는 부분과 추상화만으로도 전체적인 협력 방식을 결정할 수 있다.