캡슐화란 데이터와 관련된 기능을 묶는 것을 말한다.
캡슐화는 객체가 기능을 어떻게 구현했는지 외부에 감추는 것이다.
구현에 사용된 데이터의 상세 내용을 외부에 감춘다.
예전에는 정보 은닉과 구분해서 얘기했지만, 최근에는 정보 은닉의 의미를 포함한다고 표현한다.
if (acc.getMembership() == REGULAR && acc.getExpDate().isAfter(now())) {
... 정회원 기능
}
위 코드에서 getMembership() == REGULAR를 왜REGULAR인지 확인하는지 의도를 단번에 알 수 없다.
그럼 수정할 때 또 다시 그 의도를 확인해봐야한다. (시간과 비용이 늘어나는 것은 당연한 일..)
만약, 5년 이상 사용자에게도 일부 기능 정회원 혜택을 1개월 무상 제공하는 코드로 변경해야한다면..?
if (acc.getMembership() == REGULAR &&
(
(acc.getServiceData().isAfter(fiveYearAgo)
&& acc.getExpDate().isAfter(now())) ||
(acc.getServiceDate().isBefore(fiveYearAgo)
&& addMonth(acc.getExpDate()).isAfter(now()))
)
) {
... 정회원 기능
}
이런 코드를 나에게 수정하라 했으면 정말 끔찍했을거다..
요구 사항의 변화가 데이터 구조 및 사용에 변화를 발생시킨다.
따라서, 데이터를 사용하는 모든 코드에서 수정이 발생해야만 한다.
기능을 제공하고 구현 상세를 감춘다.
public class Account {
private Membership membership;
private Date expDate;
public boolean hasRegularPermission() {
return membership == REGULAR
&& expDate.isAfter(now))
}
}
if(acc.hasRegularPermission()) {
... 정회원 기능
}
위 코드에서 같은 조건으로 혜택 기준이 바뀌어도 메서드를 사용하는 if문은 그대로 유지하고, Account 객체의 내부 구현만 수정하면 된다.
public class Account {
private Membership membership;
private Date expDate;
public boolean hasRegularPermission() {
return membership == REGULAR &&
(expDate.isAfter(now)) ||
(
serviceDate.isBefore(fiveYearAgo()) &&
addMonth(expDate).isAfter(now))
)
);
}
}
요구 사항이 변화해도 Account의 내부 구현만 변경하면 되기 때문에, 캡슐화된 기능을 사용하는 여러 부분의 코드들에 영향을 최소화시킨다.
캡슐화는 기능에 대한 (의도 파악) 이해를 높인다.
acc.getMembership() 로 데이터를 가져와 회원이 REGULAR인지 확인한다면, 왜 확인하는지 의도까지는 파악하지 못한다.
해당 코드를 이해하기 위해선 관련된 코드를 전부 읽고 이해해야 수정이 가능하다.
하지만 acc.hasRegularPermission()라고 작성한다면, 해당 검사는 계정이 REGULAR 권한을 가졌는지 확인하기 위함임을 단번에 파악할 수 있다.
데이터 달라 하지 말고, 해달라고 하기
// 나쁜 예시
if(acc.getMembership() == REGUALR) {
... 정회원 기능
}
위 코드는 멤버십 데이터를 달라고 요청하여, regular인지 본 코드에서 확인한다.
이를 캡슐화 규칙을 적용하면,
if(acc.hasRegularPermission()) {
... 정회원 기능
}
위 코드처럼 이 데이터가 regular인지 판단하여 리턴해달라고 지시하는 코드로 작성하는 것이 좋다.
캡슐화 : 내부 기능의 구현을 외부에 감춘다.
캡슐화를 통해 기능을 사용하는 코드에 영향을 주지 않고 (또는 최소화) 내부 구현을 변경할 수 있는 유연함