-
[Refactoring] 7. 뒤엉킨 변경클린코드 2022. 9. 10. 20:34
| 인프런 - 백기선님의 코딩으로 학습하는 리팩토링 강의를 수강하며 정리한 글입니다.
뒤엉킨 변경
- 소프트웨어는 변경에 유연하게 대처할 수 있어야 한다.
- 서로 다른 문제는 다른 모듈에서 해결하는 것이 좋다.
- 응집도를 높이고 결합도를 낮추자.
- 응집도를 높이고 결합도를 낮추자.
- 모듈의 책임이 분리되어 있을 수록 해당 문맥을 더 잘 이해할 수 있으며 다른 문제는 신경쓰지 않아도 된다.
- 관련 리팩토링 기술
- 단계 쪼개기 -> 서로 다른 문맥의 코드를 분리할 수 있다.
- 함수 옮기기 -> 적절한 모듈로 함수를 옮길 수 있다.
- 함수 추출하기 -> 여러 가지 작업이 하나의 함수에 모여 있을 경우 적용할 수 있다.
- 클래스 추출하기 -> 별도의 클래스로 분리할 수 있다.
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