클래스 추출

두 클래스가 처리해야 할 기능이 하나의 클래스에 들어 있을 땐(하나의 클래스에서 2가지 일 이상을 수행할 땐)
새 클래스를 만들고 기존 클래스의 관련 필드와 메서드를 새 클래스로 옮기자

동기

  • 클래스는 확실하게 추상화되어야 하며, 두세 가지의 명확한 기능을 담당해야 한다
  • 하지만 개발자는 클래스에 점증적으로 어떤 기능이나 데이터를 추가하기 때문에, 클래스는 시간이 갈수록 방대해지기 마련이다
  • 이런 클래스는 너무 많은 메서드와 데이터가 들어있어 이해하기 힘드므로, 분리할 부분을 궁리해서 떼어내야 한다
  • 데이터의 일부분과 메서드의 일부분이 한 덩어리이거나, 주로 함께 변화하거나 서로 유난히 의존적인 데이터의 일부분일 경우 클래스로 떼어내기 좋다

방법

  1. 위의 동기를 참조해서 클래스의 기능 분리 방법을 정한다
  2. 분리할 기능을 넣을 새 클래스를 작성한다
    • 기능이 분리됨으로써 원본 클래스의 이름이 변경될 수 있다
  3. 원본 클래스에서 새 클래스로의 링크를 만든다
    • 필요할 때 까지 역방향 링크를 만들지 않는다
  4. 옮길 필드마다 필드 이동을 적용한다
  5. 메서드 이동을 적용한다
  6. 각 클래스를 다시 검사해서 인터페이스를 줄인다
    • 양방향 링크를 단방향 링크로 바꿀 수 있는지 등
  7. 추출 된 클래스의 공개 범위를 결정한다
    • 공개할 경우 몇가지

예시

아래와 같은 코드가 있다

1
2
3
4
5
6
7
8
9
10
class Person {
private String name;
private String officeAreaCode;
private String officeNumber;
// getter/setter

public String getTelephoneNumber() {
return "(" + officeAreaCode + ")" + officeNumber;
}
}

officeAreaCodeofficeNumber를 합쳐서 TelephoneNumber로 분리하기로 한다
필드 이동을 적용하기 전에 필드 자체 캡슐화를 적용한다

1
2
3
4
5
6
7
8
9
10
class Person {
private String name;
private String officeAreaCode;
private String officeNumber;
// getter/setter

public String getTelephoneNumber() {
return "(" + getOfficeAreaCode() + ")" + getOfficeNumber();
}
}

새로 생성한 클래스에 필드 이동을 적용한다

1
2
3
4
5
class TelephoneNumber {
private String areaCode;
private String number;
// getter/setter
}

Person에서 officeAreaCode, officeNumber를 삭제한다
이때 getter는 위임 코드로 전환한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
private TelephoneNumber officeTelephone;
private String name;

public String getOfficeAreaCode() {
return officeTelephone.getAreaCode();
}

public String getOfficeNumber() {
return officeTelephone.getNumber();
}

public String getTelephoneNumber() {
return "(" + getOfficeAreaCode() + ")" + getOfficeNumber();
}
}

getTelephoneNumber()메서드 이동을 적용한다

1
2
3
4
5
6
7
8
9
class TelephoneNumber {
private String areaCode;
private String number;
// getter/setter

public String getTelephoneNumber() {
return "(" + areaCode + ")" + number;
}
}

Person을 정리한다

1
2
3
4
5
6
7
8
class Person {
private TelephoneNumber officeTelephone;
private String name;

public String getTelephoneNumber() {
return officeTelephone.getTelephoneNumber();
}
}

여기서 TelephoneNumber에 대한 공개 여부에 대한 이야기가 나오는데 잘 이해가 가지 않는다
외부에 공개하면 불변 객체로 만들어서 수정이 불가능하게끔 해야하고,
참조 객체로 만드려면 외부에 공개하지 않아야 하는것이 아닐까?
모르겠당…

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)