데이터 중심 설계와 책임 중심 설계의 차이
객체를 단순한 데이터의 집합으로 바라보는 시각은 객체의 내부 구현을 퍼블릭 인터페이스에 노출시키는 결과를 낳는다.
결과적으로 설계가 객체들의 내부 구현 변경에 취약해진다.
데이터 중심 설계에서는 객체의 행위보다 상태를 우선 생각한다.
데이터 중심의 관점에서 객체는 자신의 데이터를 조작하는데 필요한 오퍼레이션을 정의한다.
데이터 중심 설계에서도 형식적인 데이터 캡슐화는 가능하다.
객체에서 스스로의 데이터는 private으로 숨기고 대신 접근자와 수정자(getter, setter)를 드러내는 것이다.
그러나 이는 public으로 드러낸 것과 마찬가지이며 진짜 캡슐화가 아니다.
모든 인스턴스 변수를 접근자로 드러내기 때문에 그러한 인스턴스 변수들의 정보를 퍼블릭 인터페이스에 노골적으로 드러내는 것이다.
데이터 중심 설계는 객체가 스스로의 상태를 책임지지 않으므로 특정 객체에게 제어를 맡겨버리기 때문에 결과적으로 절차 지향 프로그래밍을 낳는다.
즉, 데이터 중심 설계의 가장 대표적인 문제점은 캡슐화 원칙 위반이다.
이것 하나가 낮은 응집도와 높은 결합도로 이어지고, 많은 문제를 야기한다.
설계할 때 협력에 관해 고민하지 않으면 캡슐화를 위반하는 과도한 접근자와 수정자를 가지게 되는 경향이 있다.
이처럼 접근자와 수정자에 과도하게 의존하는 설계 방식을 추측에 의한 설계 전략이라고 부른다.
이 전략은 객체가 사용될 협력을 고려하지 않고 객체가 다양한 상황에서 사용될 수 있을 것이라는 막연한 추측을 기반으로 설계를 진행한다.
결국 변경과 유지보수성!
훌륭한 객체 지향 설계는 데이터가 아닌 책임에 초점을 맞춰야 한다. 이유는 변경과 관련이 있다.
유지보수성이 목표다. 여기서 유지보수성이란 두려움 없이, 주저함 없이, 저항감 없이 코드를 변경할 수 있는 능력을 말한다. 가장 중요한 동료는 캡슐화다.
높은 응집도와 낮은 결합도를 가진 설계를 추구해야 하는 이유는 단 한가지다.
설계를 변경하기 쉽게 만들기 때문이다.
변경의 관점에서 응집도란 변경이 발생할 때 모듈 내부에서 발생하는 변경의 정도로 측정할 수 있다.
하나의 변경을 수용하기 위해 모듈 전체가 함께 변경된다면 응집도가 높은 것이다. 반면에 다수의 모듈들 일부가 함께 변경되어야 한다면 응집도가 낮은 것이다.
변경의 관점에서 결합도란 한 모듈이 변경되기 위해 다른 모듈의 변경을 요구하는 정도로 측정할 수 있다.
결합도가 높으면 높을수록 함께 변경해야 하는 모듈으 수가 늘어나기 때문에 변경하기에 어려워진다.
반면 퍼블릭 인터페이스를 수정할 때만 다른 모듈에 영향을 미치는 경우는 결합도가 낮다고 표현한다.
결합도가 높아도 상관 없는 경우도 있다. 일반적으로 변경될 확률이 매우 적은 안정적인 모듈에 의존하는 것은 아무런 문제도 되지 않는다.
자바의 Spring, ArrayList는 변경될 확률이 매우 낮기 때문에 결합도에 대해 고민할 필요가 없다.
그러나 직접 작성한 코드는 얘기가 다르다.
직접 작성한 코드는 항상 불안정하며 언제라도 변경될 가능성이 크다.
코드를 완성한 그 순간부터 코드를 수정할 준비를 해야 한다.
진정한 캡슐화
사실 캡슐화는 변경될 수 있는 어떤 것이라도 감추는 것을 의미한다.
내부 속성을 외부로부터 감추는 것은 '데이터 캡슐화'라고 불리는 캡슐화의 한 종류일 뿐이다.
그것이 속성의 타입이건, 할인 정책의 종류건 상관 없이 내부 구현의 변경으로 인해 외부 객체가 영향을 받는다면 캡슐화를 위반한 것이다.
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 Money calculateAmountDiscountedFee() {
if (movieType != MovieType.AMOUNT_DISCOUNT) {
throw new IllegalArgumentException();
}
return fee.minus(discountAmount);
}
public Money calculatePercentDiscountedFee() {
if (movieType != MovieType.PERCENT_DISCOUNT) {
throw new IllegalArgumentException();
}
return fee.minus(fee.times(discountPercent));
}
public Money calculateNoneDiscountedFee() {
if (movieType != MovieType.NONE_DISCOUNT) {
throw new IllegalArgumentException();
}
return fee;
}
// ...
}
Movie는 내부 구현을 인터페이스에 노출시키고 있다.
위 3개의 메서드는 할인 정책에 금액, 비율, 미적용 정책 3가지가 존재한다는 사실을 만천하에 드러내고 있다.
따라서 Movie는 내부 구현을 성공적으로 캡슐화하지 못했다.
데이터 중심 설계의 문제점
데이터 중심 설계가 변경의 취약한 이유는 2가지다.
- 너무 이른 시기에 데이터에 관해 결정하도록 강요
- 협력을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 강요
데이터 중심 설계 방식에 익숙한 개발자들은 일반적으로 데이터와 기능을 분리하는 절차적 프로그래밍 방식을 따른다.
이 설계 방식에서 객체는 그저 데이터의 집합체일 뿐이고, 이로 인해 접근자와 수정자를 과도하게 추가하게 된다.(추측에 의한 설계 전략)
그 결과로 객체의 캡슐화는 사실상 무너지게 되어 낮은 응집도와 높은 결합도를 갖게 되며 OCP 원칙을 위반하게 된다.
올바른 객체 지향 설계의 무게 중심은 항상 객체의 내부가 아니라 외부에 맞춰져 있어야 한다.
'CS > OOP' 카테고리의 다른 글
[오브젝트] 6장 - 메시지와 인터페이스 (0) | 2024.01.22 |
---|---|
[오브젝트] 5장 : 책임 할당과 책임 중심 설계 (0) | 2024.01.18 |
[오브젝트] 3장 - 역할, 책임, 협력 (0) | 2024.01.11 |
[오브젝트 스터디] 2장 - 상속과 다형성 (0) | 2024.01.10 |
[오브젝트 스터디] 1장 - 응집도는 높게 결합도는 낮게 (0) | 2024.01.01 |