본문 바로가기
Java

Objects 시리즈_4장.설계 품질과 트레이드 오프

by Jordy-torvalds 2020. 4. 22.
반응형

객체지향 설계란 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다. 이 정의에는 객체지향 설계에 관한 두 가지 관점이 섞여있다. 첫 번째 관점은 객체지향 설계의 핵심은 책임이라는 것이다. 두 번째 관점을 책임을 할당하는 작업이 응집도와 결합도 같은 설계 품질과 깊이 연관되어 있다.

설계는 변경을 위해 존재하고 변경에는 어떤 식으로든 비용이 발생한다 훌륭한 설계란 합리적인 비용 안에서 변경을 수용할 수 있는 구조를 만든느 것이다. 적절한 비용 안에서 쉽게 변경할 수 있는 설계는 응집도가 높고 서로 느슨하게 결합돼 있는 요소로 구성된다.

결합도의 응집도를 합리적인 수준으로 유지할 수 있는 중요한 원칙이 있다. 객체의 상태가 아니라 객체의 행동에 초점을 맞추는 것이다. 객체를 단순한 데이터의 집합으로 바라보는 시각은 객체의 내부 구현을 퍼블릭 인터페이스에 노출시키는 결과를 낳기 때문에 결과적으로 설계가 변경에 취약해진다.

이런 문제를 피할 수 있는 가장 좋은 방법은 객체의 책임에 초점을 맞추는 것이다. 책임은 객체의 상태에서 행동으로, 나아가 객체와 객체 사이의 상호작용으로 설계 중심을 이동시키고, 결합도가 낮고 응집도가 높으며 구현을 효과적으로 캡슐화하는 객체들을 창조할 수 있는 기반을 제공한다.


캡슐화

캡슐화란 변경 가능성이 높은 부분 (상태와 행동 등)을 객체 내부로 숨기는 추상화 기법이다.

변경될 가능성이 높은 부분을 구현이라고 하고 상대적으로 안정적인 부분을 인터페이스라고 부른다,

캡슐화를 하는 가장 큰 이유는 유지보수성이다. 시스템의 한 부분을 다른 부분으로부터 감춤으로써 뜻 밖의 피해가 발생할 수 있는 가능성을 사전에 방지할 수 있다.

캡슐화를 지키면 모듈 안의 응집도는 높아지고 모듈 사이의 결합도는 낮아진다.


응집도 (cohesion)

  • 모듈에 포함된 내부 요소들이 연관돼 있는 정도.
  • 모듈 내의 요소들이 하나의 목표를 위해 긴밀하게 협력한다면 그 모듈은 높은 응집도를 가지고, 모듈 내의 요소들이 서로 다른 목적을 추구한다면 그 모듈은 낮은 응집도를 가진다.
  • 객체지향의 관점에서 응집도는 객체 또는 클래스에 얼마나 관련 높은 책임을 할당했는지를 나타낸다.

결합도 (coupling)

  • 의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 지식을 갖고 있는지를 나타내는 척도다.
  • 어떤 모듈이 다른 모듈에 대해 너무 자세한 부분까지 알고 있다면 두 모듈은 높은 결합도는 가지고, 어떤 모듈이 다른 모듈에 대해 필요한 지식만 알고 있다면 두 모듈은 낮은 결합도는 가진다.
  • 객체 지향의 관점에서 결합도는 객체 또는 클래스가 협력에 필요한 적절한 수준의 관계만을 유지하고 있는지를 나타낸다.

설계 관점에서의 응집도와 결합도

좋은 설계

좋은 설계란 오늘의 기능을 수행하면서 내일의 변경을 수용할 수 있는 설계다. 그리고 좋은 설계를 만들기 위해서는 높은 응집도와 낮은 결합도를 추구해야 한다.

응집도와 결합도

높은 응집도와 낮은 결합도를 가진 설계를 추구해야 하는 이유는 단 한가지다. 설계를 변경하기 쉽게 만들기 때문이다.

변경의 관점에서 응집도변경이 발생했을 때 모듈 내부에서 발생하는 변경의 정도로 측정할 수 있다. 응집도가 높을 수록 변경의 대상과 범위가 명확해지기 때문에 코드를 변경하기 쉬워진다. 변경으로 인해 수정되는 부분을 파악하기 위해 코드 구석구석을 헤매고 다니거나 여러 모듈을 동시에 수정할 필요가 없으며 변경을 반영하기 위해 오직 하나의 모듈만 수정하면 된다.

 

변경의 관점에서 결합도한 모듈이 변경되기 위해 다른 모듈의 변경을 요구하는 정도로 측정할 수 있다. 다시 말해 하나의 모듈을 수정할 때 얼마나 많은 모듈을 함께 수정해야 하는지를 나타낸다. 따라서 결합도가 높으면 높을수록 함께 변경해야 하는 모듈의 수가 늘어나기 때문에 변경하기가 어려워 진다.

결합도가 높아도 상관 없는 경우도 있다. 일반적으로 변경될 확률이 매우 적은 안정적인 모듈에 의존하는 것은 아무런 문제도 되지 않는다. 표준 라이브러리에 포함된 모듈이나 성숙 단계에 접어든 프레임워크에 의존하는 경우가 여기에 속한다. 예를 들어, 자바의 String이나 ArrayList 는변경될 확률이 낮기 때문에 결합도에 대해 고민할 필요가 없다.


추측에 의한 설계 전략 (design-by-guessing strategy)

접근자와 수정

자에 과도하게 의존하는 설계 방식을 말한다. 이 전략은 객체가 사용될 협력을 고려하지 않도 객체가 다양한 상황에서 사용될 수 있을 것이라는 막연한 추측을 기반으로 설계를 진행한다. 따라서 프로그래머는 내부 상태를 드러내는 메서드를 최대한 많이 추가해야 한다는 압박에 시달릴 수 밖에 없으며 결과적으로 대부분의 내부 구현이 퍼블릭 인터페이스에 그대로 노출될 수 밖에 없다. 그 결과, 캡슐화의 원칙을 위반하는 변경에 취약한 설계를 얻게 된다.


단일 책임 원칙 (Single Responsibility Principle, SRP)

단일 책임 원칙을 한 마디로 요약하면 클래스는 단 한가지의 변경의 이유만 가져야 한다는 것이다. 여기서 책임은 객체 지향 설계 원칙의 삼 요소 중 하나인 책임이 아닌 변경의 이유라는 의미로 사용된다는 점이다.

ex) 영화 예매하는 메소드 내에 할인 정책을 선택하는 코드와 할인 조건을 판단하는 코드가 함께 있을 경우 새로운 할인 정책을 추가하는 작업이 할인 조건에도 영향을 줄 수 있다. ****어떤 코드를 수정한 후에 아무런 상관도 없는 코드에 문제가 발생하는 것은 모듈의 응집도가 낮을 때 발생하는 대표적인 증상이다.


스스로 자신의 데이터를 책임지는 객체

우리가 상태와 행동을 객체라는 하나의 단위로 묶는 이유는 객체 스스로 자신의 상태를 처리할 수 있게 하기 위해서다, 객체는 단순한 데이터 제공자가 아니다. 객체 내부에 저장되는 데이터보다 객체가 협력에 참여하면서 수행할 책임을 정의하는 오퍼레이션이 더 중요하다.

따라서 객체를 설계할 때 "이 객체가 어떤 데이터를 포함해야 하는가?" 라는 질문은 다음과 같은 두 개의 개별적인 질문으로 분리해야 한다.

  1. 객체가 어떤 데이터를 포함해야 하는가?
  2. 이 객체가 데이터에 대해 수행해야 하는 오퍼레이션(메소드)는 무엇인가.
반응형