[리팩터링 2판] 7. 캡슐화
해당 내용은 리팩터링 2판을 읽고 개인적으로 정리 및 개인적으로 느낀 내용입니다.
레코드 캡슐화하기
- 간단한 레코드 예제
// 캡슐화 전
class Oraganization {
constructor(data) {
this._name = data.name;
this._country = data.country;
}
get name() { return this._name;}
set name(arg) { this._name = arg; }
}
// 캡슐화 이후
class Oraganization {
constructor(data) {
this._data = data;
}
get name() { return this._data.name;}
set name(arg) { this._data._name = arg; }
}
- 중복된 레코드 예제
- 내부 객체를 복사해서 사용
-> 데이터의 구조가 클수록 비용이 올라간다.
-> 클라이언트가 원본을 수정한다고 착각할 가능성이 있다.(해결: readonly 제공 || 복제본 동결) - 레코드 캡슐화를 재귀적으로 실행
컬렉션 캡슐화하기
1. 변수 캡슐화
2. 컬렉션에 원소를 추가/제거하는 함수를 추가
3. 정적 검사 수행
4. 컬렉션을 참조하는 부분을 모두 찾고 컬렉션의 변경자를 호출하는 코드가 모두 앞에서 추가한 추가/제거 함수를 호출하도록 수정한다. =>** setter가 불필요**해진다.
5. 컬렉션의 getter를 수정하여 원본 내용을 수정할 수 없는 readonly proxy 또는 복제본을 반환하도록 한다.
기본형 -> 객체로 변경
-
변수 캡슐화
get priority() { return this._priority;} set priority(aString) { return this._priority = aString;}
-
class 생성
class Priority { constructor(value) { this._value = value;}; toString() { return this._value; } // getter }
-
정적 검사 수행
-
getter & setter 수정
get priority() { return this.priority.toString();} set priority(aString) { this._priority = new Priority(aString);}
-
함수 변경
class Order {
get priorityString() { return this._priority.toString(); }
set priority(aString) { this._priority = new Priority(aString);}
}
임시 변수를 질의 함수로 변경
// 리팩토링 전
constructor(quantity, item) {
this._quantity = quantity;
this._item = item;
}
get price() {
var basePrice = this._quantity * this._item.price;
var discountFactor = 0.98;
if (basePrice > 1000) discountFactor -= 0.03;
return basePrice * discountFactor;
}
// 리팩토링 후
get price () {
return this.basePrice * this.discountFactor;
클래스 추출 (<-> 클래스 인라인)
특정 데이터, 메소드를 일부 제거해도 논리적으로 문제가 없다면 클래스를 분리할 것.
// 변경 전
class Person {
get name() { return this._name; }
set name(arg) { this._name = arg; }
get telephoneNumber() { return `(${this.officeAreaCode});
get officeAreaCode() { return this_officeAreaCode; }
get officeNumber() { return this._officeNumber; }
set officeNumber(arg) { this_officeNumber = arg; }
}
// 변경 후
class Person {
}
class TelephoneNumber {
get areaCode() { return this._areaCode; }
set areaCode(arg) { this.areaCode = arg; }
get number() { return this._number; }
set number(arg) { this.number = arg; }
toString() { return `(${this.areaCode}) ${this.number}`;}
}
class Person {
get officeAreaCode() {..}
set officeAreaCode(arg) {..}
get officeNumber() {..}
set officeNumber(arg) {..}
get telephoneNumber() {return this._telephoneNumber.toString();
}
클래스 인라인 (<-> 클래스 추출하기)
클래스의 남은 역할이 거의 없는 경우 가장 많이 사용하는 클래스로 흡수시키는 방법
EX) TrackingInformation.class < Shipment.class 흡수
위임 숨기기 (<-> 중재자 제거하기)
ex) 어떤 사람이 속한 부서의 관리자를 알고싶다.
-> 부서 클래스는 관리자 정보를 제공한다.
// 위임 숨기기 전
class Person {
get name() {}
set name(arg) {}
get department() {}
set department(arg) {}
}
class Department {
...,
get manager() {}
set manager(arg) {}
}
manager = aPerson.department.manager;
// 위임 숨기기
class Person {
get name() {}
set name(arg) {}
get manager() {return this._department.manager;}
}
manager = aPerson.manager;
중재자 제거하기 (<-> 위임 숨기기)
// Person.class가 단지 위임용으로만 사용됨.
class Person {
get manager() { return this._department.manager;}
}
manager = aPerson.manager;
// 중재자 제거 후
manager = aPerson.department.manager;
알고리즘 교체하기
-> 일반적으로 내가 생각하고 있던 리팩터링의 개념
// 리팩터링 전
function foundPerson(people) {
for(let i=0; i<people.length; i++) {
if (people[i] === "Don") {
return "Don";
}
...
}
}
// 리팩터링 후
function foundPerson(people) {
const candidates = ["Don", "..", ".."];
return people.find(p => p.candidates.includes(p)) || "";
}
Author And Source
이 문제에 관하여([리팩터링 2판] 7. 캡슐화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yeoj1n/리팩터링-2판-7.-테스트-구축하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)