[리팩토링] 악취 9 : 기능 편애

 

✍️ 악취 9 : 기능 편애

어떤 메서드가 자신이 속한 클래스보다 다른 클래스를 더 많이 참조한다면 이를 '기능 편애'라고 한다. 기능 편애 현상이 나타나는 가장 큰 이유는 올바르지 못한 데이터 구조에서 기인한다. 한 클래스가 너무 많은 기능을 담당하거나, 필드와 메서드가 서로 다른 클래스에 분리된 경우가 그 대표적인 예시이다.

 

여기 기능 편애를 해결하기 위한 리팩토링 기법이 있다.

1. "함수 옮기기" 올바른 책임을 위해

 

🍊 함수 옮기기

전기 사용량과 가스 사용량을 표현하는 두 클래스가 있고

@AllArgsConstructor
@Getter
public class ElectricityUsage {

    private double amount;

    private double pricePerUnit;
}

@AllArgsConstructor
@Getter
public class GasUsage {

    private double amount;

    private double pricePerUnit;
}

 

총 사용료를 계산하는 Bill 클래스가 있다. 현재 구조에서 아래 Bill 클래스가 기능 편애 악취를 가지고 있다. 정확히 calculateBill 메서드가 사용료 계산이라는 기능 편애 욕심을 내고 있다. 여기엔 문제가 있는데 가령 가스 사용량에 따른 계산 방법이 변경되었다면 아래와 같은 가스 사용료 계산 코드를 모두 수정해야 하는 불상사가 발생한다.

public class Bill {

    private ElectricityUsage electricityUsage;

    private GasUsage gasUsage;

    public double calculateBill() {
        double electricityBill = electricityUsage.getAmount() * electricityUsage.getPricePerUnit();
        double gasBill = gasUsage.getAmount() * gasUsage.getPricePerUnit();

        return electricityBill + gasBill;
    }

}

 

따라서 Bill에서 사용량과 가격을 참조해서 공과금을 계산하는 것보단, 각각의 클래스에서 사용료를 계산하는 것이 더 올바른 책임이라고 생각한다. 이러한 구조는 수정에도 유연한데, 만약 가스 사용료 계산 방법이 변경되더라도 getGasBill의 코드만 수정하면 된다.

// after!

@AllArgsConstructor
@Getter
public class ElectricityUsage {

    private double amount;

    private double pricePerUnit;

    public double getElectricityBill() {
        return amount * pricePerUnit;
    }

}


@AllArgsConstructor
@Getter
public class GasUsage {

    private double amount;

    private double pricePerUnit;

    public double getGasBill() {
        return amount * pricePerUnit;
    }
}

public class Bill {

    private ElectricityUsage electricityUsage;

    private GasUsage gasUsage;

    public double calculateBill() {
        return electricityUsage.getElectricityBill() + gasUsage.getGasBill();
    }
}