캡슐화(Encapsulation)

이 글과 하위 글들은 [객체 지향 프로그래밍 입문]에 관한 내용입니다. 최범균님의 인프런의 강의를 보며 정리한 내용으로 문제가 될시 삭제하겠습니다.

  • 데이터 + 관련 기능 묶기

  • 객체가 기능을 어떻게 구현했는지 외부에 감추는 것. 구현에 사용된 데이터의 상세 내용을 외부에 감춤

  • 정보 은닉(Information Hiding) 의미 포함

  • 외부에 영향 없이 객체 내부 구현 변경 가능

캡슐화하지 않으면,

기존 요구 사항이 아래와 같은데,

if(acc.getMembership() === "REGULAR" && acc.getExpDate().isAfter(now())) {
  // ...정회원 기능
}

요구사항 추가: 5년 이상 사용자, 일부 기능 정회원 혜택 1개월 무상 제공

if(acc.getMembership() === "REGULAR" && (
  (acc.getServiceDate().isAfter(fiveYearAgo) && acc.getExpDate().isAfter(now())) || 
  (acc.getServiceDate().isBefore(fiveYearAgo) && addMonth(acc.getExpDate()).isAfter(now()))
)) {
  // ...정회원 기능
}

위와 같은 코드를 추가하게 된다. 더 큰 문제는 위와 같이 캡슐화가 안된 코드에서는 대부분 저런 코드를 추가해야될 상황이라면 여러 곳을 수정해야할 확률이 크다.

요구사항 변경 예)

  • 장기 사용자에게 특정 기능 실행 권한을 연장(단 유효 일자는 그대로 유지)

  • 계정을 차단하면 모든 실행 권한 없음

  • Date를 LocalDateTime으로 변경

캡슐화하면?

기능을 제공하고 구현 상세를 감춘다.

if(acc.hasRegularPermission()) {
  // ...정회원 기능
}

// Account.ts
class Account {
  private membership: Membership;
  private expDate: Date;

  hasRegularPermission(): boolean {
    return this.membership === "REGULAR" && this.expDate.isAter(now())
  }
}

만약 위와 같이 요구사항이 바뀌었다면 Account 클래스 내부의 구현체만 변경하면 된다.

class Account {

  hasRegularPermission(): boolean {
    return (
      this.membership === "REGULAR" &&
      (this.expDate.isAter(now()) ||
        (this.serviceDate.isBefore(fiveYearAgo()) &&
          addMonth(expDate).isAfter(now())))
    );
  }
}

즉, 캡슐화를 잘하면 요구사항의 변화에 따른 연쇄적인 변경 전파를 최소화할 수 있다.

또한 캡슐화를 시도하게 되면, 기능에 대한 (의도) 이해를 높일 수 있다.

캡슐화를 위한 규칙

  1. Tell, Don't Ask - 데이터를 달라하지 말고 해달라고 하기

2. Demeter's Law

  • 메서드에서 생성한 객체의 메서드만 호출

  • 파라미터로 받은 객체의 메서드만 호출

  • 필드로 참조하는 객체의 메서드만 호출

정리

  • 캡슐화: 기능의 구현을 외부에 감춤

  • 캡슐화를 통해 기능을 사용하는 코드에 영향을 주지 않고 (또는 최소화) 내부 구현을 변경할 수 있는 유연함

Last updated