냄새 10. 데이터 뭉치 (Data Clumps)
- etc/리팩토링
- 2023. 5. 10.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
냄새 10. 데이터 뭉치 (Data Clumps)
- 항상 뭉쳐 다니는 데이터는 한 곳으로 모아두는 것이 좋다.
- 여러 클래스에 존재하는 비슷한 필드 목록 (비슷한 필드는 데이터 뭉치일 수도 있음)
- 여러 함수에 전달하는 매개변수 목록
- 관련 리팩토링 기술
- '클래스 추출하기'를 사용해 여러 필드를 하나의 객체나 클래스로 모을 수 있다.
- '매개변수 객체 만들기' 또는 '객체 통째로 넘기기'를 사용해 메서드 매개변수를 개선할 수 있다.
데이터 뭉치들이 있다면, 우선은 그 녀석들을 클래스로 묶어볼 수 있다는 것을 의미한다. 그리고 이런 데이터 뭉치가 여러 클래스에 존재한다면, 해당 데이터 뭉치를 클래스에서 제거하는 형태로 리팩토링을 할 수 있다. 이 리팩토링의 의미는 여러 클래스에 있는 비슷한 필드를 한 곳으로 모을 수 있다는 점이다. 이것은 클래스 추출하기 관점이다.
만약 여러 함수에 전달하는 매개변수의 목록이 비슷한 경우라면, 매개변수 객체 만들기 + 객체 통째로 넘기기를 이용해서 메서드의 매개변수 숫자를 줄일 수 있다.
코드
아래 코드를 살펴보자. PersonalAreaCode, PersonalNumber는 항상 같이 다니는 변수다. 따라서 데이터 뭉치로 볼 수 있기 때문에 하나의 클래스로 리팩토링 할 수 있다. 아래에서 리팩토링 순서를 살펴보고자 한다.
public class Office {
private String location;
// areaCode + Number가 함께 사용됨.
// Office 클래스에도 존재함.
private String officeAreCode;
private String officeNumber;
public Office(String location, String officeAreCode, String officeNumber) {
this.location = location;
this.officeAreCode = officeAreCode;
this.officeNumber = officeNumber;
}
public String officePhoneNumber() {
return officeAreCode + "-" + officeNumber;
}
public String getOfficeAreCode() {
return officeAreCode;
}
public void setOfficeAreCode(String officeAreCode) {
this.officeAreCode = officeAreCode;
}
public String getOfficeNumber() {
return officeNumber;
}
public void setOfficeNumber(String officeNumber) {
this.officeNumber = officeNumber;
}
}
아래와 같이 리팩토링 작업을 할 수 있다.
- TelephoneNumber 클래스를 생성하고, 그곳으로 변수를 빼낸다. TelephoneNumber에는 새로운 변수가 추가되었기 때문에 적절한 Getter / Setter를 생성해준다.
- 기존의 Employee 클래스에는 PersonalPhoneNumber를 필드로 추가하고, 관련된 생성자를 생성한다.
- Empolyee 클래스에서 personalAreaCode / personalNumber를 삭제하자. 그리고 기존에 사용하던 Getter / Setter는 삭제하면 된다. 왜냐하면 해당 메서드는 이미 TelephoneNumber 클래스로 이동했기 때문이다.
- 만약 코드를 그대로 남겨둬야 한다면, TelephoneNumber를 통해 Deligate 할 수 있다.
이제 Employee 클래스와 TelephoneNumber 클래스의 리팩토링이 끝났다. 같은 데이터 뭉치를 쓰고 있던 Office 클래스도 동일한 방식으로 리팩토링 하면 된다. 결과는 아래와 같이 된다.
public class Office {
private String location;
private TelephoneNumber officePhoneNumber;
public Office(String location, TelephoneNumber officePhoneNumber) {
this.location = location;
this.officePhoneNumber = officePhoneNumber;
}
}
public class Employee {
private String name;
private TelephoneNumber personalPhoneNumber;
// areaCode + Number가 함께 사용됨.
public Employee(String name, TelephoneNumber telephoneNumber) {
this.name = name;
this.personalPhoneNumber = telephoneNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TelephoneNumber {
private String personalAreaCode;
private String personalNumber;
public TelephoneNumber(String personalAreaCode, String personalNumber) {
this.personalAreaCode = personalAreaCode;
this.personalNumber = personalNumber;
}
public String getPersonalAreaCode() {
return personalAreaCode;
}
public void setPersonalAreaCode(String personalAreaCode) {
this.personalAreaCode = personalAreaCode;
}
public String getPersonalNumber() {
return personalNumber;
}
public void setPersonalNumber(String personalNumber) {
this.personalNumber = personalNumber;
}
public String phoneNumber() {
return this.personalAreaCode + "-" + this.personalNumber;
}
}
FutureMore → 문맥 점검.
위에서 리팩토링이 완료된 것처럼 보이지만, 디테일한 부분이 완료되지 않았다. 클래스로 변수를 뺀 후에, 변수의 이름이 Context에 맞는지를 확인하는 작업이 남았다.
TelephoeNumber 클래스에는 PersonalAreaCode, PersonalPhoneNumber 필드가 있다. 이 녀석들은 Employee 클래스에서 가져온 변수인데, 해당 클래스에 있을 때는 문맥상 맞는 변수였다. 하지만 TelephoneNumber 클래스의 필드 관점에서는 적절하지 않다. 왜냐하면 개인 전화번호가 아니라 사무실 전화번호도 될 수 있기 때문이다.
따라서 문맥에 맞는 필드명과 메서드명으로 수정해주면, TelephoneNumber 클래스의 최종 모습은 다음과 같다.
public class TelephoneNumber {
private String areaCode;
private String phoneNumber;
public TelephoneNumber(String areaCode, String phoneNumber) {
this.areaCode = areaCode;
this.phoneNumber = phoneNumber;
}
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String phoneNumber() {
return this.areaCode + "-" + this.phoneNumber;
}
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 30. 기본형을 객체로 바꾸기 (0) | 2023.05.10 |
---|---|
냄새 11. 기본형 집착 (0) | 2023.05.10 |
냄새 9. 기능 편애 (0) | 2023.05.10 |
리팩토링 29. 클래스 인라인 (0) | 2023.05.10 |
리팩토링 28. 함수 인라인 (0) | 2023.05.10 |