리팩터링 전
class Customer {
get plan() {return this._plan;}
get discountRate() {return this._discountRate;}
리팩터링 후
class Customer {
get plan() {return this._plan;}
get discountRate() {return this.plan.discountRate;}
🧷 배경
프로그램의 진짜 힘은 데이터 구조에서 나온다. 주어진 문제에서 적합한 데이터 구조를 활용하면 동작 코드는 자연스럽게 단순하고 직관저그로 짜여진다.
데이터 구조가 적절치 않음을 깨닫게 되면 곧바로 수정해야 된다.
레코드 (또는 클래스, 객체)를 변경하는 작업은 더 큰 변경의 일환으로 수행되는데 당연히 해당 필드를 호출하는 모든 코드에 대해서 수정해야하기 때문이다. 당장 필드를 옮길 수 없을 경우는 사용 패턴을 먼저 리팩터링 한 다음 필드를 옮겨준다.
데이터의 위치를 옮기더라도 접근자만 그에 맞게 수정하면 클라이언트 코드들은 아무 수정 없이도 동작할 것이다.
🧷 절차
1. 소스 필드가 캡슐화 되어 있지 않다면 캡슐화 한다. (직접 접근이 아닌 접근자를 사용)
2. 테스트 한다.
3. 타깃 객체에 필드(와 접근자 메서드)를 생성한다.
4. 정적 검사를 수행한다.
5. 소스 객체에서 타깃 객체를 참조할 수 있는지 확인한다.
6. 접근자들이 타겟 필드를 사용하도록 수정한다.
7. 테스트 한다.
8. 소스 필드를 제거한다.
9. 테스트 한다.
🧷 예시
// Customer 클래스...
constructor(name, discountRate) {
this._name = name;
this._discountRate = discountRate;
this._contract = new CustomerContract(dateToday());
}
get discountRate() {return this._discountRate;}
becomePreferred() {
this._discountRate += 0.03;
// 다른 멋진 일들
}
applyDiscount(amount) {
return amount.subtract(amount.multiply(this._disountRate));
}
// CustomerContract 클래스 ...
constructor(startDate) {
this._startDate = startDate;
}
// Customer 클래스...
constructor(name, discountRate) {
this._name = name;
this._setDiscountRate(discountRate);
this._contract = new CustomerContract(dateToday());
}
get discountRate() {return this._discountRate;}
_setDiscountRate(aNumber) {this._discountRate = aNumber;}
becomePreferred() {
this._setDiscountRate(this.discountRate + 0.03);
// 다른 멋진 일들
}
applyDiscount(amount) {
return amount.subtract(amount.multiply(this.disountRate));
}
CustomerContract
클래스에 필드 하나와 접근자들을 추가한다.
// CustomerContract 클래스 ...
constructor(startDate, discountRate) {
this._startDate = startDate;
this._discountRate = discountRate;
}
get discountRate() {return this._discountRate;}
set discountRate(arg) {this._discountRate = arg;}
Customer
의 접근자들이 새로운 필드를 사용하도록 수정한다.
// Customer 클래스...
constructor(name, discountRate) {
this._name = name;
this._contract = new Customer(dateToday()); // Contract 객체를 생성하고 setDiscountRate를 호출해야 하므로 순서를 바꿔준다.
this._setDiscountRate(discountRate);
}
// Customer 클래스...
get discountRate() {return this._contract.discountRate;}
_setDiscountRate(aNumber) {this._contract.discountRate = aNumber;}
🧷 예시: 공유 객체로 이동하기
// Account 클래스 ...
constructor(number, type, interestRate) {
this._number = number;
this._type = type;
this.interestRate = interestRate;
}
get interestRate() {return this._interestRate;}
// AccountType 클래스 ...
constructor(nameString) {
this._name = nameString;
}
이자율 필드는 캡슐화 되어 있으니 AccountType
에 이자율 필드와 필요한 접근자 메서드를 생성해보자.
// AccountType 클래스 ...
constructor(nameString, interestRate) {
this._name = nameString;
this._interestRate = interestRate;
}
get interestRate() {return this._interestRate;}
이자율을 가져오도록 수정하면 종류가 같은 모든 계좌가 이자율을 공유하게 된다. 겉보기 동작이 달라지므로 좌 클래스에 어서션을 추가해준다.
// Account 클래스 ...
constructor(number, type, interestRate) {
this._number = number;
this._type = type;
assert(interestRate === this._type.interestRate);
this._interestRate = interestRate;
}
get interestRate() {return this._interestRate;}
겉보기 동작이 달라지지 않는 것을 확인 한 후 Account에서 이자율을 직접 수정하는 코드를 제거한다.
// Account 클래스 ...
constructor(number, type) {
this._number = number;
this._type = type;
}
get interestRate() {return this._type.interestRate;}
Last updated