객체지향 설계의 핵심은 협력, 역할, 책임이다. 이 중에서도 가장 중요한 것이 '책임'이다. 책임은 객체지향 애플리케이션의 품질을 결정한다.
객체지향 설계란 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 것이다. 즉, 객체지향 설계의 핵심이 책임이고 책임을 할당하는 과정이 응집도, 결합도와 연관되어있다.
설계라는 것은 변경을 위해 존재하고 변경 시에는 반드시 비용이 발생한다. 훌륭한 설계란 적절한 비용 안에서 쉽게 변경할 수 있는 응집도가 높고 서로 느슨하게 결합된 요소로 구성된 설계이다.
그렇다면 응집도를 높이기 위해선 어떡하면 되는 것일까???
객체의 상태가 아니라 객체의 행동에 초점을 두면 된다. 다시 말해 객체의 책임에 초점을 두는 것이다. (이는 데이터 중심 설계와 반대된다.)
설계가 필요한 이유는 요구사항이 변경되기 때문이다. 그런데 데이터 중심의 설계는 변경에 취약하다. 객체의 상태(=데이터)는 구현에 속한다. 그런데 구현은 불안정하기 때문에 변하기 쉽다. 이러한 상태의 변경은 인터페이스의 변경을 초래하고 해당 인터페이스에 의존하는 모든 객체에 영향이 퍼지게 된다. 따라서 데이터 중심의 설계는 변경에 취약하다.
반면에, 책임 주도 설계는 상대적으로 변경에 안정적인 설계를 얻을 수 있다. 책임은 인터페이스에 속한다. 안정적인 인터페이스 뒤에 상태를 캡슐화함으로써 구현 변경에 대한 파급효과를 방지한다. 따라서 책임 주도 설계는 변경에 안정적이다.
import java.util.List;
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;// 할인 조건 직접 정의
}
// 할인 정책의 종류를 결정한다public enum MovieType {
AMOUNT_DISCOUNT,// 금액 할인 정책
PERCENT_DISCOUNT,// 비율 할인 정책
NONE_DISCOUNT// 미적용
}
데이터 중심의 설계는 객체가 내부에 저장해야 하는 '데이터가 무엇인가'부터 시작된다. 그러다 보니 Movie 안에 직접 정의되는 차이들이 발생했다. 데이터 중심 설계에서는 이처럼 객체의 종류를 저장하는 인스턴스 변수와 인스턴스 종류에 따라 배타적으로 사용될 인스턴스 변수를 하나의 클래스에 포함시키는 방식이 사용된다.
그런데, 객체지향에서 가장 중요한 원칙은 캡슐화이다. 따라서 캡슐화를 지키기 위해 접근자와 수정자를 추가한다.
import java.util.Collections;
import java.util.List;
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;// 할인 조건 직접 정의public MovieType getMovieType() {
return movieType;
}
public void setMovieType(MovieType movieType) {
this.movieType = movieType;
}
public Money getFee() {
return fee;
}
public void setFee(Money fee) {
this.fee = fee;
}
public List<DiscountCondition> getDiscountConditions() {
return Collections.unmodifiableList(discountConditions);
}
public void setDiscountConditions(List<DiscountCondition> discountConditions) {
this.discountConditions = discountConditions;
}
public Money getDiscountAmount() {
return discountAmount;
}
public void setDiscountAmount(Money discountAmount) {
this.discountAmount = discountAmount;
}
public double getDiscountPercent() {
return discountPercent;
}
public void setDiscountPercent(double discountPercent) {
this.discountPercent = discountPercent;
}
}
지금 데이터 중심 설계를 하는 이유는 책임 주도 설계와의 장단점을 비교하기 위해서다. 이를 판단하기 위한 척도로 캡슐화, 응집도, 결합도를 사용한다.
캡슐화는 외부에서 알 필요가 없는 부분을 감춤으로써 대상을 단순화하는 추상화의 한 종류다. 객체지향은 불안정한 구현의 세부사항을 안정적인 인터페이스의 뒤로 캡슐화한다.
설계는 요구사항이 변경되기 때문에 필요하다. 캡슐화는 변경으로 인한 파급효과를 통제하기 위해 사용한다. 따라서 변경의 관점에서 설계의 품질을 판단하기 위해 캡슐화를 기준으로 삼는다. 그리고 객체지향 설계의 핵심은 변경될 수 있는 어떤 것이라도 캡슐화해야 한다는 것이다.