리팩토링 26. 함수 옮기기
- etc/리팩토링
- 2023. 5. 10.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
리팩토링 26. 클래스 추출하기(Extract Class)
- 클래스가 다루는 책임이 많아질수록 클래스가 점차 커진다.
- 클래스를 쪼개는 기준
- 데이터나 메서드 중 일부가 매우 밀접한 관련이 있는 경우
- 몇몇 데이터가 대부분 같이 바뀌는 경우
- 데이터 또는 메서드 중 일부를 삭제한다면 어떻게 될 것인가?
- 필드나 메서드를 한번 추출해보면, 같이 빠져나가야 하는게 무엇인지 컴파일 에러로 확인할 수 있음.
- 하위 클래스를 만들어 책임을 분산 시킬 수도 있다.
- 클래스를 옮긴 후 해야 할 일
- 추출된 클래스에 있는 필드 / 메서드의 문맥을 확인함. (메서드, 필드 이름은 적절한지?)
- 이전 클래스의 메서드 중, 추출된 클래스의 변수를 많이 참조하는 것이 있으면 추출된 클래스로 이동.
- 전체적인 접근 지시자, final 키워드등을 점검한다.
- 순환 참조가 발생하는지도 점검
- 정리
- Refactor → Extract Deligate로 일단 추출해봄.
- 필요한 필드 / 메서드를 클래스로 옮김
- 문맥을 고려한 필드명 / 메서드를 확인. 접근 지시자 확인.
- 기존 클래스 정리.
- 기존 클래스에 있던 메서드는 Deligation을 통해 처리 / 메서드 통째로 삭제 등으로 접근할 수 있음.
클래스가 하는 일이 많아지면 클래스가 커진다. 너무 큰 클래스는 코드 복잡도가 높기 때문에 읽기 어렵다. 그렇기 때문에 트스트 하기도 어렵고, 클래스가 하는 일을 이해하기도 어려워진다. 따라서 이런 경우 클래스를 나눠서 코드 복잡도를 분산시킬 수 있다. 큰 클래스에서 다른 클래스를 추출해내는 작업을 해야하는 것이다.
- 클래스에 들어있는 필드 / 메서드들이 밀접하게 관련 있는 경우.
- 필드나 메서드를 바꿀 때, 같이 변경되는 경우. (필드나 메서드를 한번 빼내보면, 같이 빠져나가야 하는 게 뭐가 있는지 컴파일 에러로 볼 수 있다.)
코드
Person 클래스를 살펴보면 officeAreaCode, officeNumber 필드는 관련이 있어 보인다. 관련있는 두 필드를 다른 클래스로 추출해보려고 한다면, 어떤 메서드 + 필드가 함께 추출되어야 하는지 알 수 있게 된다.
public class Person {
private String name;
// 아래 두 필드는 관련이 있다. 이것을 extract deligate로 빼보자.
// 빼보면 어떤 메서드 / 필드가 함께 나가야하는지 알 수 있음.
private String officeAreaCode;
private String officeNumber;
public String telephoneNumber() {
return this.officeAreaCode + " " + this.officeNumber;
}
public String name() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String officeAreaCode() {
return officeAreaCode;
}
public void setOfficeAreaCode(String officeAreaCode) {
this.officeAreaCode = officeAreaCode;
}
public String officeNumber() {
return officeNumber;
}
public void setOfficeNumber(String officeNumber) {
this.officeNumber = officeNumber;
}
}
아래의 Refactor → Extract Deligate를 클릭하면 특정 필드를 특정 클래스로 추출할 수 있다.
이렇게 필드를 다른 클래스로 추출하고 나면, 기존 클래스에서는 필드에 영향을 받은 메서드들이 컴파일 에러를 보여주며 남아있다. 이 때 해당 메서드들을 처리하는 방법은 두 가지가 존재한다.
- 해당 메서드를 그대로 놔두고, 추출한 클래스로부터 Deligation을 통해서 처리한다.
- 해당 메서드를 삭제한다.
기존 클래스에서 컴파일 에러를 보여주는 메서드는 일단 추출한 클래스로 함께 복사시켜야 한다. 그리고 남은 메서드를 어떻게 처리할지를 고민해야한다. 위 두 가지 방법 중 하나를 고려해야한다.
그 다음으로 고려해야 할 부분은 새롭게 추출된 클래스에 있는 필드 / 메서드들이 문맥을 살펴보는 것이다. 예를 들어 officeAreaCode, officeNumber는 Person 클래스에서 사용될 때는 맞는 의미였다. 그렇지만 TelephoneNumber 클래스 관점에서 살펴보면 오피스 전화번호 일 수도 있고, 개인 전화번호를 표현할 수도 있게 되고 그렇다. 따라서 TelephoneNumber라는 클래스 이름을 고려해서 필드이름을 officeAreaCode → areaCode 등으로 수정해줘야한다.
최종 코드
최종 코드를 살펴보면 다음과 같이 변경되는 것을 확인할 수 있다. 먼저 기존 Person 클래스다.
public class Person {
private final TelephoneNumber telephoneNumber;
private String name;
public Person(TelephoneNumber telephoneNumber, String name) {
this.telephoneNumber = telephoneNumber;
this.name = name;
}
public String name() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String telephoneNumber() {
return this.telephoneNumber.toString();
}
}
새롭게 추출된 TelephoneNumber 클래스다.
public class TelephoneNumber {
// 필드이름이 TelephoneNumber 클래스에 걸맞지 않다.
private String areaCode;
private String phoneNumber;
public TelephoneNumber(String areaCode, String phoneNumber) {
this.areaCode = areaCode;
this.phoneNumber = phoneNumber;
}
public String officeAreaCode() {
return this.areaCode;
}
public void setOfficeAreaCode(String officeAreaCode) {
this.areaCode = officeAreaCode;
}
public String officeNumber() {
return this.phoneNumber;
}
public void setOfficeNumber(String officeNumber) {
this.phoneNumber = officeNumber;
}
@Override
public String toString() {
return this.areaCode + " " + this.phoneNumber;
}
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 27. 필드 옮기기 (0) | 2023.05.10 |
---|---|
냄새 8. 산탄총 수술 (0) | 2023.05.10 |
리팩토링 25. 함수 옮기기 (0) | 2023.05.10 |
리팩토링 24. 단계 쪼개기 (0) | 2023.05.10 |
냄새 7. 뒤엉킨 변경 (0) | 2023.05.10 |