리팩토링 22. 여러 함수를 변환 함수로 묶기
- etc/리팩토링
- 2023. 5. 2.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
리팩토링 22. 여러 함수를 변환 함수로 묶기(Combine Functions into Transform)
- 관련있는 여러 파생 변수를 만들어내는 변환 함수가 여러 곳에서 만들어지고 사용된다면, 그러한 파생 변수들을 '변환 함수'를 통해 한 곳으로 모아둘 수 있다.
- 소스 데이터가 가변이라면, '여러 함수를 클래스로 묶기(트랜스포머)'를 사용하는 것이 적절하다.
- 소스 데이터가 불변이라면, 아래 두 가지 방법이 가능하다.
- 여러 함수를 클래스로 묶기
- 변환 함수를 사용해서 불변 필드로 생성해두고 재사용.
관련있는 여러 파생변수들을 만들어내는 함수가 여러 곳에서 사용된다면, 변환 함수를 통해서 파생 변수를 한 군데로 모으는 방법이다. 최종적인 목표는 소스 데이터를 변환함수를 통해 파생 변수를 만들어 내는 작업이다. 그리고 이런 함수들은 하나의 클래스(Transformer)에 모여서 여기저기서 재사용할 수 있도록 만든다. 그래서 우리가 굳이 파생변수를 만들어내는 여러 함수들을 여러 곳에서 반복해서 사용할 필요가 없도록 줄여주는 기능이다.
주로 소스가 가변 데이터이면서, 여러 클래스에서 가변 데이터 생성하는 변환 함수가 사용되면 함수를 클래스로 묶는 것이 타당하다. 소스 데이터가 매번 변하기 때문에, 파생변수를 사용하지 않고 클래스로 묶고 메서드를 제공해서 이를 통해 파생변수를 매번 계산하는 것이 좋다.
만약 소스 데이터가 변경되지 않는 경우라면 다음 두 가지를 이용하면 모두 적절하다.
- 메서드를 클래스로 묶어서 처리
- 변환함수를 통해서 불변 데이터의 필드로 생성해서 사용할 수 있음.
소스 데이터가 매번 변하는 경우라면, 클래스로 묶어서 클래스의 메서드를 통해서 파생 변수를 그 때마다 계산해내는 것이 적절하다.
코드
Reading 레코드는 불변타입이며, 클라이언트 1 ~ 3은 비슷한 가변 파생 변수를 생성한다.
- 클라이언트 1 : Base Charge 계산
- 클라이언트 2 : Base Charge 계산 + TaxableCharge 계산
- 클라이언트 3 : Base Charge 계산 (메서드로 분리)
각각의 클라이언트들이 동일한 메서드들을 가지므로 상위 클래스로 올려서 처리하는 방법이 있다. 그렇지만 이번에는 트랜스폼과 새로운 데이터 타입을 만들어내는 해결 방법을 적용해본다.
public class Client1 {
double baseCharge;
// baseCharge : 가변 파생 필드. 여기저기서 사용됨.
public Client1(Reading reading) {
this.baseCharge = baseRate(reading.month(), reading.year()) * reading.quantity();
}
private double baseRate(Month month, Year year) {
return 10;
}
public double getBaseCharge() {
return baseCharge;
}
}
public class Client2 {
private double base;
private double taxableCharge;
// baseCharge : 가변 파생 필드. 여기저기서 사용됨.
// taxableCharge : 가변 파생 필드. 여기저기서 사용됨.
public Client2(Reading reading) {
this.base = baseRate(reading.month(), reading.year()) * reading.quantity();
this.taxableCharge = Math.max(0, this.base - taxThreshold(reading.year()));
}
private double taxThreshold(Year year) {
return 5;
}
private double baseRate(Month month, Year year) {
return 10;
}
public double getBase() {
return base;
}
public double getTaxableCharge() {
return taxableCharge;
}
}
...
리팩토링을 위해서 EnrichReading이라는 레코드를 생성한다. EnrichReading은 Reading 레코드 뿐만 아니라 가변 파생 변수인 Base Charge, Taxable Charge를 가지는 레코드로 만든다. 이 레코드는 계산된 값을 가지는 객체가 된다. 앞으로 이런 식으로 동작하게 될 것 이다.
- 클라이언트는 트랜스포머를 통해 값을 조회한다.
- 트랜스포머는 새로운 타입 EnricingReading을 통해서 값을 전달한다.
public record EnrichingReading(Reading reading, double baseCharge, double taxableCharge) {
}
변환함수를 가진 트랜스 포머 객체 ReadingClient 클래스를 생성한다. 이 클래스는 파생 변수 생성을 위한 메서드 (변환함수)를 가지고 있고, 변환함수에 필요한 내부 메서드들을 가진 클래스가 된다. 트랜스포머 클래스는 파생 변수를 제공하는 대신, EnrichingReading 이라는 객체를 통해 메서드 형태로 계산된 값을 제공한다.
public class ReadingClient {
private double taxThreshold(Year year) {
return 5;
}
private double baseRate(Month month, Year year) {
return 10;
}
// EnrichingReading 인스턴스 생성
protected EnrichingReading enrichingReading(Reading reading) {
return new EnrichingReading(reading,
calculatedBaseCharge(reading),
calculatedTaxableCharge(reading));
}
private double calculatedBaseCharge(Reading reading) {
return baseRate(reading.month(), reading.year());
}
private double calculatedTaxableCharge(Reading reading) {
return Math.max(0, this.calculatedBaseCharge(reading) - taxThreshold(reading.year()));
}
}
이제 각 Client는 ReadingClient라는 트랜스포머 객체를 통해서 값을 조회해서 사용하면 된다. 예를 들면 아래와 같이 조회해서 사용할 수 있다.
public class Client1 extends ReadingClient{
private double base;
// baseCharge : 가변 파생 필드. 여기저기서 사용됨.
public Client1(Reading reading) {
this.base = enrichingReading(reading).baseCharge();
}
public double getBaseCharge() {
return this.base;
}
}
이 리팩토링의 의미
- 파생 변수를 생성하는 비슷한 변환함수가 여러 클래스에 퍼져있었다.
- 변환함수들을 하나의 클래스로 모았고, 이 클래스는 Transformer가 되었다.
- 여러 클래스는 파생 변수를 직접 생성하는 것이 아니라, 트랜스포머에 요청해서 값을 받아쓴다. 트랜스포머 객체는 요청이 오면, 그 즉시 값을 계산해서 전달한다.
- 이 때, 트랜스포머는 새로운 불변 객체(레코드)를 제공하고, 클라이언트는 레코드를 통해서 필요한 값을 조회해서 사용할 수 있다.
결과적으로 여러 클래스에 퍼진 비슷비슷한 변환 함수를 한 군데로 모으고, 파생 변수를 모두 제거하는 작업이 완료되었다.
'etc > 리팩토링' 카테고리의 다른 글
냄새 7. 뒤엉킨 변경 (0) | 2023.05.10 |
---|---|
리팩토링 23. 참조를 값으로 바꾸기 (0) | 2023.05.02 |
리팩토링 21. 파생 변수를 질의 함수로 바꾸기 (0) | 2023.05.02 |
리팩토링 20. 세터 제거하기 (0) | 2023.05.01 |
리팩토링 19. 질의 함수와 변경 함수 분리하기 (0) | 2023.05.01 |