| 인프런 - 백기선님의 코딩으로 학습하는 리팩토링 강의를 수강하며 정리한 글입니다.
뒤엉킨 변경
- 소프트웨어는 변경에 유연하게 대처할 수 있어야 한다.
- 서로 다른 문제는 다른 모듈에서 해결하는 것이 좋다.
- 응집도를 높이고 결합도를 낮추자.
- 응집도를 높이고 결합도를 낮추자.
- 모듈의 책임이 분리되어 있을 수록 해당 문맥을 더 잘 이해할 수 있으며 다른 문제는 신경쓰지 않아도 된다.
- 관련 리팩토링 기술
- 단계 쪼개기 -> 서로 다른 문맥의 코드를 분리할 수 있다.
- 함수 옮기기 -> 적절한 모듈로 함수를 옮길 수 있다.
- 함수 추출하기 -> 여러 가지 작업이 하나의 함수에 모여 있을 경우 적용할 수 있다.
- 클래스 추출하기 -> 별도의 클래스로 분리할 수 있다.
Refactoring 1. 단계 쪼개기
- 서로 다른 일을 하는 코드를 각기 다른 모듈로 분리한다. -> 그것과 관련있는 부분만 신경 쓸 수 있도록 하기 위해서
- 여러 일을 하는 함수의 처리 과정을 각기 다른 단계로 구분할 수 있다.
- ex) 전처리 -> 주요 작업 -> 후처리
- ex) 전처리 -> 주요 작업 -> 후처리
- 서로 다른 데이터를 사용한다면, 단계를 나누는데 있어 중요한 단서가 될 수 있다.
- 중간 데이터를 사용하여 단계를 구분하고, 매개변수를 줄이는데 활용할 수 있다.
Before
public class PriceOrder {
public double priceOrder(Product product, int quantity, ShippingMethod shippingMethod) {
final double basePrice = product.basePrice() * quantity;
final double discount = Math.max(quantity - product.discountThreshold(), 0)
* product.basePrice() * product.discountRate();
final double shippingPerCase = (basePrice > shippingMethod.discountThreshold()) ?
shippingMethod.discountedFee() : shippingMethod.feePerCase();
final double shippingCost = quantity * shippingPerCase;
final double price = basePrice - discount + shippingCost;
return price;
}
}
public record Product(double basePrice, double discountThreshold, double discountRate) {
}
public record ShippingMethod(double discountThreshold, double discountedFee, double feePerCase) {
}
After
public class PriceOrder {
public double priceOrder(Product product, int quantity, ShippingMethod shippingMethod) {
final PriceData priceData = calculatePriceData(product, quantity);
final double price = applyShipping(priceData, shippingMethod);
return price;
}
private static PriceData calculatePriceData(Product product, int quantity) {
final double basePrice = product.basePrice() * quantity;
final double discount = Math.max(quantity - product.discountThreshold(), 0)
* product.basePrice() * product.discountRate();
final PriceData priceData = new PriceData(quantity, basePrice, discount);
return priceData;
}
private static double applyShipping(PriceData priceData, ShippingMethod shippingMethod) {
final double shippingPerCase = (priceData.basePrice() > shippingMethod.discountThreshold()) ?
shippingMethod.discountedFee() : shippingMethod.feePerCase();
final double shippingCost = priceData.quantity() * shippingPerCase;
final double price = priceData.basePrice() - priceData.discount() + shippingCost;
return price;
}
}
public record PriceData(int quantity, double basePrice, double discount) {
}
public record Product(double basePrice, double discountThreshold, double discountRate) {
}
public record ShippingMethod(double discountThreshold, double discountedFee, double feePerCase) {
}
=> PriceData : 중간 데이터
Refactoring 2. 함수 옮기기
- 모듈화가 잘 되어 있는 소프트웨어는 최소한의 지식으로 프로그래밍이 가능하다.
- 관련있는 함수나 필드가 모여있어야 더 쉽게 찾고 활용할 수 있다.
- 함수를 옮겨야 하는 경우
- 해당 함수가 다른 문맥(클래스)에 있는 데이터를 더 많이 참조하는 경우
- 해당 함수를 다른 클라이언트(클래스)에서도 필요한 경우
Before
public class Account {
private int daysOverdrawn;
private AccountType type;
public Account(int daysOverdrawn, AccountType type) {
this.daysOverdrawn = daysOverdrawn;
this.type = type;
}
public double getBankCharge() {
double result = 4.5;
if (this.daysOverdrawn() > 0) {
result += this.overdraftCharge();
}
return result;
}
private int daysOverdrawn() {
return this.daysOverdrawn;
}
private double overdraftCharge() {
if (this.type.isPremium()) {
final int baseCharge = 10;
if (this.daysOverdrawn <= 7) {
return baseCharge;
} else {
return baseCharge + (this.daysOverdrawn - 7) * 0.85;
}
} else {
return this.daysOverdrawn * 1.75;
}
}
}
public class AccountType {
private boolean premium;
public AccountType(boolean premium) {
this.premium = premium;
}
public boolean isPremium() {
return this.premium;
}
}
After
public class Account {
private int daysOverdrawn;
private AccountType type;
public Account(int daysOverdrawn, AccountType type) {
this.daysOverdrawn = daysOverdrawn;
this.type = type;
}
public double getBankCharge() {
double result = 4.5;
if (this.daysOverdrawn() > 0) {
result += this.type.overdraftCharge(this.daysOverdrawn);
}
return result;
}
private int daysOverdrawn() {
return this.daysOverdrawn;
}
}
public class AccountType {
private boolean premium;
public AccountType(boolean premium) {
this.premium = premium;
}
public boolean isPremium() {
return this.premium;
}
double overdraftCharge(int daysOverdrawn) {
if (isPremium()) {
final int baseCharge = 10;
if (daysOverdrawn <= 7) {
return baseCharge;
} else {
return baseCharge + (daysOverdrawn - 7) * 0.85;
}
} else {
return daysOverdrawn * 1.75;
}
}
}
=> type의 isPremium()을 사용하므로 overdraftChange메서드를 Account Class 에서 AccountType Class로 이동
Refactoring 3. 클래스 추출하기
- 클래스가 다루는 책임이 많아질수록 클래스가 커진다.
- 클래스를 쪼개는 기준
- 데이터나 메서드의 일부가 매우 밀접한 경우
- 일부의 데이터, 메서드가 같이 바뀌는 경우
- 하위 클래스로 만들어 책임을 분산시킬 수 있는 경우
Before
public class Person {
private String name;
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;
}
}
After
public class Person {
private 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 TelephoneNumber getTelephoneNumber() {
return telephoneNumber;
}
}
public class TelephoneNumber {
private String areaCode;
private String number;
public TelephoneNumber(String areaCode, String officeNumber) {
this.areaCode = areaCode;
this.number = officeNumber;
}
public String areaCode() {
return areaCode;
}
public String number() {
return number;
}
}
'클린코드' 카테고리의 다른 글
[Refactoring] 9. 기능 편애 (0) | 2022.09.12 |
---|---|
[Refactoring] 8. 산탄총 수술 (0) | 2022.09.11 |
[Refactoring] 6. 가변 데이터 (0) | 2022.09.09 |
[Refactoring] 5. 전역 데이터 (0) | 2022.09.08 |
[Refactoring] 4. 긴 매개변수 목록 (0) | 2022.09.07 |