진정한 캡슐화
에 대해 알아보도록 하겠습니다.
진정한 캡슐화
제목이 조금 이상한듯 싶지만, 우리가 알고 있는 이전의 캡슐화는 진짜 캡슐화가 아닐 확률이 높습니다. 때문에 진정한 캡슐화
를 알아보자는 취지에서 이런 제목을 붙혔습니다. 우선 보통의 경우 캡슐화의 의미에 대해서 알아보겠습니다.
1. 캡슐화란 ?
캡슐화란 내부의 구현을 인터페이스를 통해 숨기는것을 말합니다. 설계중에서 변경될 가능성이 높은 부분을 구현
이라고하며, 상대적으로 안정적인 부분을 인터페이스
라고 부릅니다. 캡슐화는 이런 변경가능성이 높은 구현을 숨기고 상대적으로 안정적인 부분을 공개함으로써 변경을 최소화하도록 합니다.
2. 캡슐화가 필요한 이유?
상태와, 행동을 객체라는 하나의 단위로 묶는 이유는, 객체 스스로 자신의 상태를 처리할 수 있게 하기 위해서입니다. 달리말해 객체는 단순한 데이터 제공자가 아니며, 자신의 상태를 스스로 처리하는 것입니다.
객체가 자신의 상태를 스스로 처리하지 않고, 만약 내부의 구현을 외부로 노출하고, 외부에서 이를 수정가능하도록 한다면 최악의 설계 결과인 높은 결합도 낮은 응집도를 가진 어플리케이션이 탄생할 것이며, 이는 곳 변경에 취약한 어플레이케이션이 될 것입니다. 이를 위해서 우리는 캡슐화를 해야 합니다.
우리는 캡슐화를 통해서 외부로부터의 접근을 제어 및 통제할 수 있고 이를 통해 객체를 자율적인(자신의 상태를 스스로 관리하는) 존재로 만들 수 있고, 결과적으로 변경에 용이한 설계를 이룰 수 있을 것입니다.
3. 가짜 캡슐화
그렇다면 다시 주제로 되돌아와서, 진정한 캡슐화를 알아보기 이전에, 그렇다면 진짜 캡슐화가 아닌 경우는 어떤 것인지를 알아보도록 하겠습니다.
가짜 캡슐화 1
xxxxxxxxxx
public class Movie {
private Money fee;
public Money getFee() {
return this.fee;
}
public void setFee(Money fee) {
this.fee = fee;
}
}
위의 예제는 내부의 구현(상태)를 외부로 노출시키지 않았으며, 접근자, 수정자를 통해서 내부의 구현에 접근하도록 하고 있습니다. 그렇다면 위의 예제는 진정한 캡슐화일까요?? 아쉽지만, 아닙니다 외부에서 fee의 존재를 알도록 했기 때문입니다. 만약 fee의 타입이 변경된다면, 이 접근자와 수정자를 이용하는 모든 객체가 수정이 필요하게 됩니다. 위와 같이 내부 속성을 외부로부터 감추는 것은 데이터 캡슐화
로 캡슐화의 한 종류일 뿐입니다.
가짜 캡슐화 2
x
public class DiscountCondition {
private DiscountConditionType type;
private int sequence;
private DayOfWeek dayOfWeek;
private LocalTime startTime;
private LocalTime endTime;
public DiscountConditionType getType() { ... }
public boolean isDiscountable(DayOfWeek dayOfWeek, LocalTime time) { ... }
public boolean isDiscountable(int sequence) { ... }
}
위의 isDiscountable() 메소드들은 DiscountCondition 객체가 가지고있는 상태와 비교하여, 할인 조건에 부합하는지를 판단하고 할인 여부를 반환하는 메소드들입니다. 위의 예제는 캡슐화가 잘 되었다고 착각을 하기에 충분해 보입니다. 하지만 이역시도 캡슐화가 제대로 이루어지지 않은 예입니다.
isDiscountable() 메소드들의 시그니쳐를 보면 파라미터를 통해 외부에 내부의 구현을 노출하고 있음을 알 수 있습니다. 이 상황에서 DiscountCondition의 내부 속성들을 변경한다면 isDiscountable() 을 사용하는 외부의 객체들 역시 변경이 필요할 것입니다. 내부 구현의 변경을 통해 외부의 변경이 발생한다면 캡슐화가 부족하다는 증거입니다.
가짜 캡슐화 3
xxxxxxxxxx
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() { ... }
public Money calculateAmountDiscountedFee() { ... }
public Money calculatePercentDiscountedFee() { ... }
public Money calculateNoneDiscountedFee() { ... }
}
위의 예제는 어떨까요? 메소드의 파라미터나 반환값으로 내부에 포함된 어떠한 속성도 나타내지 않고 있습니다. 아쉽게도 위의 예제 역시 캡슐화가 잘 되지 않은 예입니다.
위 예제역시 내부의 구현을 인터페이스에 노출시키고 있는데요, 바로 할인정책의 종류입니다. 만약 할인정책이 추가되거나 제거가 된다면 어떨까요?? 따라서 할인정책 세가지를 포함하고 있다는 내부구현을 외부에 노출함으로써 캡슐화가 제대로 이루어지지 않았음을 알 수 있습니다.
4. 진정한 캡슐화
진정한 캡슐화는 외부로 부터, 내부의 구현을 숨기고 인터페이스에 정의된 메서드를 통해서만 상태에 접근하도록 하는것을 말합니다. 여기서 메서드는 단순히 속성 하나의 값을 변경하고, 반환하는 접근자 수정자를 말하지 않습니다. 여기서의 메서드는 객체가 책임져야 하는 어떤 일을 수행하기 위해 내부의 상태를 변경하는 메서드를 말하는 것입니다.
내부 구현의 변경으로 인해 외부의 객체가 영향을 받는다면 캡슐화를 위반한 것입니다. 따라서 설계에서 변하는것이 무엇인지를 고려하고 변하는 개념을 캡슐화
해야 합니다.
'Software Engineering > OOAD' 카테고리의 다른 글
OOAD - 데이터 주도 설계의 문제점(결합도와 응집도) (0) | 2020.01.27 |
---|---|
OOAD - 책임, 역할, 협력을 이용한 객체지향 설계 (0) | 2020.01.26 |