# 7.5 클래스 추출하기 (Extract Class)

리팩터링 전

```javascript
class Person{
  get officeAreaCode() {return this._officeAreaCode;}
  get officeNumber() {return this._officeNumber;}
}
```

리팩터링 후

```javascript
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;}
}
```

## 🧷 배경

실무에서 연산을 추가하고 데이터를 보강하다보면 클래스는 점점 비대해져서 복잡해지게 된다.&#x20;

메서드와 데이터가 너무 많은 클래스는 이해하기 어려우니 분리하는 것이 좋다. 함께 변경되는 일이 많거나 서로 의존하는 데이터들도 분리한다.

제거해도 다른 필드나 메서드들이 논리적으로 문제가 없다면 분리할 수 있다.

## 🧷 절차

1. 클래스의 역할을 분리할 방법을 정한다.
2. 분리될 역할을 담당할 클래스를 새로 만든다.
3. 원래 클래스의 생성자에서 새로운 클래스의 인스턴스를 생성하여 필드에 저장해둔다.
4. 분리될 역할에 필요한 필드들을 새 클래스로 옮긴다(필드 옮기기). 하나씩 옮길 때마다 테스트한다.
5. 메서드들도 새 클래스로 옮긴다(함수 옮기기). 이때 저수준 메서드, 즉 다른 메서드를 호출하기보다는 호출을 당하는 일이 많은 메서드부터 옮긴다. 하나씩 옮길때마다 테스트한다.
6. 양쪽 클래스의 인터페이스를 살펴보면서 불필요한 메서드를 제거하고, 이름도 새로운 환경에 맞게 바꾼다.
7. 새 클래스를 외부로 노출할지 정한다. 노출하려거든 새 클래스에 참조를 값으로 바꾸기를 적용할지 고민해본다.

## 🧷 예시

```javascript
// 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;}
```

1. 전화번호 관련 동작을 별도 클래스로 뽑아자. 빈전화번호를 표현하는 `TelephoneNumber` 클래스를 정의한다.

```javascript
class TelephoneNumber{
  }
```

2. `Person` 클래스의 인스턴스를 생성할 때 전화번호 인스턴스도 함께 생성해서 저장한다.

```javascript
// Person 클래스
constructor() {
  this._telephoneNumber = new TelephoneNumber();
}
  
// TelephoneNumber 클래스
get officeAreaCode()  {return this._officeAreaCode;}
set officeAreaCode(arg) {this._officeAreaCode = arg;}
```

3. 필드들을 하나씩 새 클래스로 옮긴다.

```javascript
// Person 클래스...
get officeAreaCode()  {return this._telephoneNumber.officeAreaCode;}
set officeAreaCode(arg)  {this._telephoneNumber.officeAreaCode = arg;}
```

4. 테스트 후 다음 필드로 넘어간다.

```javascript
// 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;}  
```

5. `telephoneNumber()` 메서드를 옮긴다.

<pre class="language-javascript"><code class="lang-javascript"><strong>// TelephoneNumber 클래스...
</strong>get telephoneNumber() {return `({this.officeAreaCode}) ${this.officeNumber}`;}

// Person 클래스...
get telephoneNumber() {return this._telephoneNumber.telephoneNumber;}
</code></pre>

6. 메서드의 이름을 바꿔준다.

```javascript
// 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;}
```

7. 읽기 좋은 포맷으로 출력하는 역할도 새로 만든 클래스에 추가한다.

```javascript
// TelephoneNumber 클래스...
toString()  {return `$({this.areaCode}) ${this.number}`;}
  
// Person 클래스...
get telephoneNumber() {return this._telephoneNumber.toString();}
```

‘office’로 시작하는 메서드들을 없애서 `TelephoneNumber`의 접근자를 바로 사용하도록 바꿀수도 있다.
