결합도와 응집도를 합리적인 수준으로 유지할 수 있는 중요한 원칙 중 하나는 객체의 상태가 아니라 객체의 행동에 초점을 맞추는 것이다. 객체를 단순한 데이터의 집합으로 바라보는 시각은 객체의 내부 구현을 노출시키는 결과를 낳기 때문에 결과적으로 설계가 변경에 취약해진다. 가끔씩은 좋은 설계보다는 나쁜 설계를 살펴보는 과정에서 통찰을 얻기도 한다. 이번 장에서는 책임이 아닌 상태를 표현하는 데이터 중심의 설계를 살펴본다. (p. 98)
데이터 중심의 설계란 객체 내부에 저장되는 데이터를 기반으로 시스템을 분할하는 방법이다. 책임 중심의 설계가 '책임이 무엇인가'를 묻는 것으로 시작한다면 데이터 중심의 설계는 객체가 내부에 저장해야 하는 '데이터가 무엇인가'를 묻는 것으로 시작한다. Movie
를 저장할 데이터에 따라 설계하면 아래와 같다. 내부의 데이터를 반환하기 위한 접근자와 데이터를 변경하는 수정자를 추가한다면, 무수히 많은 set
과 get
을 만들어 낸다. (p. 99)
public class Movie {
private String title;
private Duration runningTime;
private Money fee;
private List<DiscountCondition> discountConditions;
private MovieType movieType;
private Money discountAmount;
private double discountPercent;
결합도가 높아도 상관 없는 경우도 있다. 일반적으로 변경될 확률이 매우 적은 안정적인 묘듈에 의존하는 것은 아무런 문제도 되지 않는다. 표준 라이브러리에 포함된 모듈이나 성숙 단계에 접어든 프레임워크에 의존하는 경우가 여기에 속한다. 예를 들어 자바의 String
이나 ArrayList
는 변경될 확률이 매우 낮기 때문에 결합도에 대해 고민할 필요가 없다. (p. 112)
데이터 중심의 설계는 캡슐화 위반, 높은 결합도, 낮은 응집도와 같은 문제점들을 갖고 있다. (p. 113)
데이터 중심으로 설계한 Movie
클래스를 보면 오직 메서드를 통해서만 객체의 내부 상태에 접근할 수 있다는 것을 알 수 있다. 예를 들어, fee
의 값을 읽거나 수정하기 위해서는 getFee
메서드와 setFee
메서드를 사용해야만 한다. 아래의 코드는 직접 객체의 내부에 접근할 수 없기 때문에 캡슐화의 원칙을 지키고 있는 것처럼 보인다. 그러나 안타깝게도 접근자와 수정자 메서드는 객체 내부의 상태에 대한 어떤 정보도 캡슐화하지 못한다. getFee
메서드와 setFee
메서드는 Movie
내부에 Money
타입의 fee
라는 이름의 인스턴스 변수가 존재한다는 사실을 퍼블릭 인터페이스에 노골적으로 드러낸다. 사실 getFee
메서드를 사용하는 것은 인스턴스 변수 fee
의 가시성을 private
에서 public
으로 변경하는 것과 거의 동일하다. (p. 113)
public class Movie {
private Money fee;
public Money getFee() {
return fee;
}
public void setFee(Money fee) {
this.fee = fee;
}
}
메서드는 단순히 속성 하나의 값을 반환하거나 변경하는 접근자나 수정자를 의미하는 것은 아니다. 객체에게 의미 있는 메서드는 객체가 책임져야 하는 무언가를 수행하는 메서드다. 속성의 가시성을 private
로 설정했다고 해도 접근자와 수정자를 통해 속성을 외부로 제공하고 있다면 캡슐화를 위반하는 것이다. (p. 117)
사실 캡슐화는 변경될 수 있는 어떤 것이라도 감추는 것을 의미한다. 내부 속성을 외부로부터 감추는 것은 '데이터 캡슐화'라고 불리는 캡슐화의 한 종류일 뿐이다. 내부 구현의 변경으로 인해 외부의 객체가 영향을 받는다면 캡슐화를 위반한 것이다. 설계에서 변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화해야 한다. 정리하면 캡슐화란 변하는 어떤 것이든 감추는 것이다. 그것이 무엇이든 구현과 관련된 것이라면 말이다. (p. 128)
데이터 중심의 설계가 변경에 취약한 이유는 두 가지다. (p. 131)
SRP - Single Responsibility Principle
"A class should have one, and only one, reason to change."
로버트 마틴은 모듈의 응집도가 변경과 관련이 있다는 사실을 강조하기 위해 단일 책임 원칙이라는 설계 원칙을 제시했다.
단일 책임 원칙에서 '책임'이라는 말이 '변경의 이유'라는 의미로 사용되는데, 이는 지금까지 얘기한 협력에서 얘기하는 책임과는 다르며 변경과 관련된 더 큰 개념을 가리킨다.
변경의 이유를 하나만 갖도록 한다면 메서드가 하나만 있는 클래스를 생각할 수 있다. 극단적으로 받아들이지 말고 균형을 잘 맞추어서 설계하도록 하자.