DEV ℧ Developer Diary

[Refactoring] 냄새 10. 데이터 뭉치

해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.

냄새 10. 데이터 뭉치

  • 항상 뭉쳐 다니는 데이터는 한 곳으로 모아두는 것이 좋다.
    • 여러 클래스에 존재하는 비슷한 필드 목록
    • 여러 함수에 전달하는 매개변수 목록
  • 관련 리팩토링 기술
    • “클래스 추출하기(Extract Class)”를 사용해 여러 필드를 하나의 객체나 클래스로 모을 수 있다.
    • “매개변수 객체 만들기 (Introduce Parameter object)” 또는 “객체 통째로 넘기기 (Preserve Whole Object)를 사용해 메소드 매개변수를 개선할 수 있다.”

예제 코드

  • Employee
public class Employee {

    private String name;

    private String personalAreaCode;

    private String personalNumber;

    public Employee(String name, String personalAreaCode, String personalNumber) {
        this.name = name;
        this.personalAreaCode = personalAreaCode;
        this.personalNumber = personalNumber;
    }

    public String personalPhoneNumber() {
        return personalAreaCode + "-" + personalNumber;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

Employee 클래스에서는 데이터 뭉치의 분류를 구분하자면 전화번호중 지역코드와 전화번호인 personalAreaCode, personalNumber의 정보는 항상 같이 따라다니기 때문에 하나의 정보로 볼 수 있다.

그렇다면 전화번호에 관한 객체를 만들어 따로 추출해 주도록 하자.

TelephoneNumber 객체를 하나 만들어 지역코드(personalAreaCode)와 전화번호(personalNumber)를 따로 빼주었다. 또한 공통적으로 사용될 것을 고려하여 지역코드(personalAreaCode -> areaCode)와 전화번호(personalNumber -> number)의 변수명을 교체해 주었다.

  • TelephoneNumber
public class TelephoneNumber {

    private String areaCode;

    private String number;

    public TelephoneNumber(String personalAreaCode, String personalNumber) {
        this.areaCode = personalAreaCode;
        this.number = personalNumber;
    }

    public String getAreaCode() {
        return areaCode;
    }

    public void setAreaCode(String areaCode) {
        this.areaCode = areaCode;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String toString() {
        return this.areaCode + "-" + this.number;
    }
}

이후 Employee 클래스에서 지역코드와, 전화번호에 대한 필드를 제거한뒤 새로 만든 객체로 대체해 주도록 하자. 기존의 직원 전화번호를 반환하던 personalPhoneNumber 메소드는 TelephoneNumber 클래스에서 toString을 만들어 맞는 형식으로 변환하여 반환하도록 하였다.

public class Employee {

    private String name;

    private TelephoneNumber personalPhoneNumber;

    public Employee(String name, TelephoneNumber personalPhoneNumber) {
        this.name = name;
        this.personalPhoneNumber = personalPhoneNumber;
    }

    public String personalPhoneNumber() {
        return personalPhoneNumber.toString();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

여기서 추가적으로 덧붙이자면, 그렇다고해서 기존의 personalAreaCode, personalNumber 필드는 삭제하지만 전화번호의 수정이 있을 경우 TelephoneNumber 객체를 호출하여 변환해주는 방법도 있겠지만, 직원의 전화번호에 대한 권한을 Employee 객체에게 위임하는 방식도 사용할 수 있다.

public class Employee {

    private String name;

    private TelephoneNumber personalPhoneNumber;

    public Employee(String name, TelephoneNumber personalPhoneNumber) {
        this.name = name;
        this.personalPhoneNumber = personalPhoneNumber;
    }

    public String personalPhoneNumber() {
        return personalPhoneNumber.toString();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPersonalAreaCode() {
        return this.personalPhoneNumber.getAreaCode();
    }

    public void setPersonalAreaCode(String areaCode) {
        this.personalPhoneNumber.setAreaCode(areaCode);
    }
    public String getPersonalNumber() {
        return personalPhoneNumber.getNumber();
    }

    public void setPersonalNumber(String number) {
        this.personalPhoneNumber.setNumber(number);
    }
}