07. 캡슐화
모듈을 분리하는 가장 중요한 기준은 아마도 시스템에서 각 모듈이 자신을 제외한 다른 부분에 드러내지 않아야 할 비밀을 얼마나 잘 숨기느냐에 있을 것이다.
#
7.1 레코드 캡슐화하기레코드(딕셔너리, 맵과 같은 자료형)를 클래스화하는 것
- 레코드는 자유도 측면에선 좋지만 어떤 필드가 존재하는지, 어떻게 변경되는지 알기가 클래스보다 상대적으로 어려움
- 게터는 데이터 구조를 깊이 탐색하게 만들되 원본 데이터를 그대로 반환하지 않고, 랩핑하거나 카피해서 반환하는게 효과적일 수 있음
#
레코드 캡슐화 리팩터링 절차- 레코드를 담은 변수 캡슐화 (레코드 get 메서드를 따로 분리)
- 레코드 필드만을 가지는 클래스를 생성 및 원본 레코드를 사용하는 코드를 보고 접근제어자 생성
- 해당 클래스 테스트
- 새로 생성한 클래스를 반환하는 메서드 생성
- 위의 메서드로 기존 1번 메서드를 사용하는 지점을 변경, 1번 메서드로 부터 파생되는 접근 제어들을 새로만든 클래스 접근제어자로 대체
- 기존 원본 레코드 코드 삭제
- 테스트
- 레코드 필드가 중첩구조라면 레코드 캡슐화하기, 컬렉션 캡슐화하기를 재귀적으로 적용
#
7.2 컬렉션 캡슐화하기setter로 컬렉션 자체를 수정할 수 있었던 것을 감추고, add/remove 와 같은 메서드로 컬렉션 필드를 직접적으로 수정하지 못하도록 만드는 것
- 더 하드한 버전인 일급컬렉션도 있음
- 컬렉션 getter가 원본을 반환하지 않으므로 (혹은 변경 불가능한 리스트를 반환하므로) 클라이언트가 실수로 원본 컬렉션을 변경할 수 없다
#
컬렉션 캡슐화 리팩터링 절차- 컬렉션을 캡슐화하지 않았다면 변수 캡슐화하기(6.6) 부터 진행
- 컬렉션에 원소 추가/제거(add/remove) 함수 추가 + 해당 컬렉션 setter 제거
- 정적 검사 수행
- 컬렉션 참조부분 모두 찾아, 변경자 호출 부분을 추가/제거 함수로 대체
- 컬렉션 getter 는 읽기전용 프락시 (불변 컬렉션) 이나 복제본을 반환
- 테스트
#
7.3 기본형을 객체로 바꾸기- primitive 타입을 클래스화 하여 책임주도 개발하는 것 (책임주도 개발은 더 공부해 봐야 할듯 ㅠ)
- 저자는 단순한 출력 이상의 기능이 필요해지는 순간 기본 타입을 전용 클래스로 변경하는 편
- 새로 추출된 클래스는 새로운 동작을 담는 장소로 활용하기 위함이 큼
#
기본형을 객체로 변경 리팩터링 절차- 변수를 캡슐화하지 않았다면 캡슐화(6.6) 진행
- 단순한 값 클래스로 만듦, 생성자는 기존 값을 인수로 받고, 해당 값 getter 추가
- 정적 검사 수행
- 기존 기본형 필드를 사용하던 클래스의 setter를 2번에서 만든 값 클래스로 변경
- 기존 기본형 필드를 사용하던 클래스의 getter를 2번에서 만든 값 클래스로 변경
- 테스트
- 함수 이름을 바꾸면 원본 접근자의 동작을 더 잘 드러낼 수 있을지 검토
#
7.4 임시 변수를 질의 함수로 바꾸기- 해당 임시 변수의 계산이 다른 곳에서도 자주 쓰이리라 판단된다면 진행해보자 (애매하다면 무지성 추출)
- 장점
- 부자연스러운 의존 관계나 부수효과를 찾고 제거하는 데 도움
- 다른 함수에서 해당 계산을 재사용 -> 코드 중복 제거
- 부적합한 곳
- '이전 주소' 와 같은 스냅샷 용도 변수에는 적합하지 않음
#
임시 변수를 질의 함수로 변경 리팩터링 절차- 변수가 사용되기 전 값이 확실히 결정되는지, 변수 사용할때마다 계산로직이 다른 결과를 내지는 않는지 확인
- 읽기 전용으로 만들 수 있는 변수는 읽기 전용으로 만들기
- 테스트
- 변수 대입문을 함수로 추출
- 테스트
- 변수 인라인(6.4)으로 임시변수 제거