07. 캡슐화

모듈을 분리하는 가장 중요한 기준은 아마도 시스템에서 각 모듈이 자신을 제외한 다른 부분에 드러내지 않아야 할 비밀을 얼마나 잘 숨기느냐에 있을 것이다.

7.1 레코드 캡슐화하기#

레코드(딕셔너리, 맵과 같은 자료형)를 클래스화하는 것

  • 레코드는 자유도 측면에선 좋지만 어떤 필드가 존재하는지, 어떻게 변경되는지 알기가 클래스보다 상대적으로 어려움
  • 게터는 데이터 구조를 깊이 탐색하게 만들되 원본 데이터를 그대로 반환하지 않고, 랩핑하거나 카피해서 반환하는게 효과적일 수 있음

레코드 캡슐화 리팩터링 절차#

  1. 레코드를 담은 변수 캡슐화 (레코드 get 메서드를 따로 분리)
  2. 레코드 필드만을 가지는 클래스를 생성 및 원본 레코드를 사용하는 코드를 보고 접근제어자 생성
  3. 해당 클래스 테스트
  4. 새로 생성한 클래스를 반환하는 메서드 생성
  5. 위의 메서드로 기존 1번 메서드를 사용하는 지점을 변경, 1번 메서드로 부터 파생되는 접근 제어들을 새로만든 클래스 접근제어자로 대체
  6. 기존 원본 레코드 코드 삭제
  7. 테스트
  8. 레코드 필드가 중첩구조라면 레코드 캡슐화하기, 컬렉션 캡슐화하기를 재귀적으로 적용

7.2 컬렉션 캡슐화하기#

setter로 컬렉션 자체를 수정할 수 있었던 것을 감추고, add/remove 와 같은 메서드로 컬렉션 필드를 직접적으로 수정하지 못하도록 만드는 것

  • 더 하드한 버전인 일급컬렉션도 있음
  • 컬렉션 getter가 원본을 반환하지 않으므로 (혹은 변경 불가능한 리스트를 반환하므로) 클라이언트가 실수로 원본 컬렉션을 변경할 수 없다

컬렉션 캡슐화 리팩터링 절차#

  1. 컬렉션을 캡슐화하지 않았다면 변수 캡슐화하기(6.6) 부터 진행
  2. 컬렉션에 원소 추가/제거(add/remove) 함수 추가 + 해당 컬렉션 setter 제거
  3. 정적 검사 수행
  4. 컬렉션 참조부분 모두 찾아, 변경자 호출 부분을 추가/제거 함수로 대체
  5. 컬렉션 getter 는 읽기전용 프락시 (불변 컬렉션) 이나 복제본을 반환
  6. 테스트

7.3 기본형을 객체로 바꾸기#

  • primitive 타입을 클래스화 하여 책임주도 개발하는 것 (책임주도 개발은 더 공부해 봐야 할듯 ㅠ)
  • 저자는 단순한 출력 이상의 기능이 필요해지는 순간 기본 타입을 전용 클래스로 변경하는 편
  • 새로 추출된 클래스는 새로운 동작을 담는 장소로 활용하기 위함이 큼

기본형을 객체로 변경 리팩터링 절차#

  1. 변수를 캡슐화하지 않았다면 캡슐화(6.6) 진행
  2. 단순한 값 클래스로 만듦, 생성자는 기존 값을 인수로 받고, 해당 값 getter 추가
  3. 정적 검사 수행
  4. 기존 기본형 필드를 사용하던 클래스의 setter를 2번에서 만든 값 클래스로 변경
  5. 기존 기본형 필드를 사용하던 클래스의 getter를 2번에서 만든 값 클래스로 변경
  6. 테스트
  7. 함수 이름을 바꾸면 원본 접근자의 동작을 더 잘 드러낼 수 있을지 검토

7.4 임시 변수를 질의 함수로 바꾸기#

// before
public double calculateDiscountedPrice() {}
double basePrice = this.quantity * this.itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
return basePrice * 0.98;
}
// after
public double calculateDiscountedPrice() {
if (calculateBasePrice() > 1000) {
return calculateBasePrice() * 0.95;
}
return calculateBasePrice() * 0.98;
}
private double calculateBasePrice() {
return this.quantity * this.itemPrice;
}
  • 해당 임시 변수의 계산이 다른 곳에서도 자주 쓰이리라 판단된다면 진행해보자 (애매하다면 무지성 추출)
  • 장점
    • 부자연스러운 의존 관계나 부수효과를 찾고 제거하는 데 도움
    • 다른 함수에서 해당 계산을 재사용 -> 코드 중복 제거
  • 부적합한 곳
    • '이전 주소' 와 같은 스냅샷 용도 변수에는 적합하지 않음

임시 변수를 질의 함수로 변경 리팩터링 절차#

  1. 변수가 사용되기 전 값이 확실히 결정되는지, 변수 사용할때마다 계산로직이 다른 결과를 내지는 않는지 확인
  2. 읽기 전용으로 만들 수 있는 변수는 읽기 전용으로 만들기
  3. 테스트
  4. 변수 대입문을 함수로 추출
  5. 테스트
  6. 변수 인라인(6.4)으로 임시변수 제거
Last updated on