✏️7.2 컬렉션 캡슐화하기 (Encapsulate Collection)
리팩터링 전
class Person {
get courses() {return this._courses;}
set courses(aList) {this._courses = aList;}리팩터링 후
class Person {
get courses() {return this._courses.slice();}
addCourse(aCourse) {...}
removeCourse(aCourse) {...}🧷 배경
저자는 가변 데이터를 모두 캡슐화한다. 데이터 구조가 언제 어떻게 수정되는지 파악하기 쉬워서 필요한 시점에 데이터 구조를 변경하기도 쉬어지기 때문이다.
컬렉션 변수로의 접근을 캡슐화하면서 게터가 컬렉션 자체를 반환하도록 한다면, 그 컬렉션을 감싼 클래스가 눈치채지 못하는 상태에서 컬렉션의 원소들이 바뀌어버릴 수 있다.
add()와 remove()라는 이름의 컬렉션 변경자 메서드를 만든다.
원본 모듈 밖에서는 컬렉션을 수정하지 않는 습관을 갖고 있다면 이런 메서드를 제공하는 것만으로도 충분할 수 있다. 컬렉션 게터가 원본 컬렉션을 반환하지 않게 만들어서 클라이언트가 실수로 컬렉션을 바꿀 가능성을 차단하는 것이 낫다.
🧷 절차
아직 컬렉션을 캡슐화하지 않았다면 변수 캡슐화하기부터 한다.
컬렉션에 원소를 추가/제거하는 함수를 추가한다. ➡️ 컬렉션 자체를 통째로 바꾸는 세터는 제거한다. 세터를 제거할 수 없다면 인수로 받은 컬렉션을 복제해 저장하도록 한다.
정적 검사를 수행한다.
컬렉션을 참조하는 부분을 모두 찾는다. 컬렉션의 변경자를 호출하는 코드가 모두 앞에서 추가한 추가/제거 함수를 호출하도록 수정한다. 하나씩 수정할 때마다 테스트한다.
컬렉션 게터를 수정해서 원본 내용을 수정할 수 없는 읽기전용 프락시나 복제본을 반환하게 한다.
테스트한다.
🧷 리팩터링 전
🧷 리팩터링 후
방금 추가한 메서드를 호출하도록 아래와 같이 코드를 수정한다.
더이상 setCourses()를 사용할 일이 없으니 제거해준다. 세터를 제공해야할 특별한 이유가 있다면 아래의 코드와 같이 복제본을 필드에 저장하도록 한다.
컬렉션 관리를 책임자는 클래스라면 항상 복제본을 제공해야 한다.
Last updated