class Person{
get officeAreaCode() {return this._officeAreaCode;}
get officeNumber() {return this._officeNumber;}
}
리팩터링 후
class Person{
get officeAreaCode() {return this._telephoneNumber.areaCode;}
get officeNumber() {return this._telephoneNumber.number;}
}
class telephoneNumber {
get areaCode() {return this._areaCode;}
get number() {return this._number;}
}
🧷 배경
실무에서 연산을 추가하고 데이터를 보강하다보면 클래스는 점점 비대해져서 복잡해지게 된다.
메서드와 데이터가 너무 많은 클래스는 이해하기 어려우니 분리하는 것이 좋다. 함께 변경되는 일이 많거나 서로 의존하는 데이터들도 분리한다.
제거해도 다른 필드나 메서드들이 논리적으로 문제가 없다면 분리할 수 있다.
🧷 절차
클래스의 역할을 분리할 방법을 정한다.
분리될 역할을 담당할 클래스를 새로 만든다.
원래 클래스의 생성자에서 새로운 클래스의 인스턴스를 생성하여 필드에 저장해둔다.
분리될 역할에 필요한 필드들을 새 클래스로 옮긴다(필드 옮기기). 하나씩 옮길 때마다 테스트한다.
메서드들도 새 클래스로 옮긴다(함수 옮기기). 이때 저수준 메서드, 즉 다른 메서드를 호출하기보다는 호출을 당하는 일이 많은 메서드부터 옮긴다. 하나씩 옮길때마다 테스트한다.
양쪽 클래스의 인터페이스를 살펴보면서 불필요한 메서드를 제거하고, 이름도 새로운 환경에 맞게 바꾼다.
새 클래스를 외부로 노출할지 정한다. 노출하려거든 새 클래스에 참조를 값으로 바꾸기를 적용할지 고민해본다.
🧷 예시
// Person 클래스...
get name() {return this._name;}
set name(arg) {this._name = arg;}
get telephoneNumber() {return `(${this.officeAreaCode}) ${this.officeNumber}`;}
get officeAreaCode() {return this._officeAreaCode;}
set officeAreaCode(arg) {this._officeAreaCode = arg;}
get officeNumber() {return this._officeNumer}
set officeNumber(arg) {this._officeNumber = arg;}
전화번호 관련 동작을 별도 클래스로 뽑아자. 빈전화번호를 표현하는 TelephoneNumber 클래스를 정의한다.
class TelephoneNumber{
}
Person 클래스의 인스턴스를 생성할 때 전화번호 인스턴스도 함께 생성해서 저장한다.
// Person 클래스
constructor() {
this._telephoneNumber = new TelephoneNumber();
}
// TelephoneNumber 클래스
get officeAreaCode() {return this._officeAreaCode;}
set officeAreaCode(arg) {this._officeAreaCode = arg;}
필드들을 하나씩 새 클래스로 옮긴다.
// Person 클래스...
get officeAreaCode() {return this._telephoneNumber.officeAreaCode;}
set officeAreaCode(arg) {this._telephoneNumber.officeAreaCode = arg;}
테스트 후 다음 필드로 넘어간다.
// TelephoneNumber 클래스...
get officeNumber() {return this._officeNumber;}
set officeNumber(arg) {this._officeNumber = arg;}
// Person 클래스...
get officeNumber() {return this._telephoneNumber.officeNumber;}
set officeNumber(arg) {this._telephoneNumber.officeNumber = arg;}
telephoneNumber() 메서드를 옮긴다.
// TelephoneNumber 클래스...
get telephoneNumber() {return `({this.officeAreaCode}) ${this.officeNumber}`;}
// Person 클래스...
get telephoneNumber() {return this._telephoneNumber.telephoneNumber;}
메서드의 이름을 바꿔준다.
// TelephoneNumber 클래스...
get areaCode() {return this._areaCode;}
set areaCode(arg) {this._areaCode = arg;}
get number() {return this._number;}
set number(arg) {return this._number = arg;}
// Person 클래스...
get officeAreaCode() {return this._telephoneNumber.areaCode;}
set officeAreaCode(arg) {this._telephoneNumber.areaCode = arg;}
get officeNumber() {return this._telephoneNumber.number;}
set officeNumber(arg) {this._telephoneNumber.number = arg;}
읽기 좋은 포맷으로 출력하는 역할도 새로 만든 클래스에 추가한다.
// TelephoneNumber 클래스...
toString() {return `$({this.areaCode}) ${this.number}`;}
// Person 클래스...
get telephoneNumber() {return this._telephoneNumber.toString();}
‘office’로 시작하는 메서드들을 없애서 TelephoneNumber의 접근자를 바로 사용하도록 바꿀수도 있다.