Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- RxCocoa
- Protocol
- Clean Code
- uiscrollview
- 리펙토링
- Refactoring
- ribs
- uitableview
- map
- swift documentation
- combine
- 스위프트
- 리펙터링
- tableView
- clean architecture
- UITextView
- 리팩토링
- Observable
- collectionview
- HIG
- Xcode
- UICollectionView
- ios
- rxswift
- 클린 코드
- 애니메이션
- SWIFT
- MVVM
- swiftUI
- Human interface guide
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[Refactoring] 5-4. 기본적인 리펙토링 (단계 쪼개기) 본문
리펙토링 핵심
- 각 방법들을 '왜' 수행해야 하는지 깨닫고 유연하게 적용하기
단계 쪼개기
- 서로 다른 두 대상을 한꺼번에 다루는 코드를 발견하면 각각을 별개 기능으로 나누는 방법
- 코드를 수정할 때 두 대상을 동시에 생각할 필요 없이 하나에만 집중할 수 있게끔하기 위함
- 기능이 잘 분리되어 있다면 다른 기능의 상세 내용은 전혀 기억하지 못해도 원하는 대로 수정을 쉽게 접근이 가능
- 단계 쪼개기 전략
- 1) 입력을 단순한 형태로 변경 (입력이 처리 로직에 적합하지 않은 형태로 들어오는 경우, 먼저 입력값을 다루기 편한 형태로 가공할것)
- 2) 처리 로직을 순차적인 단계들로 분리하고 이 단계는 서로 확연히 다른 일을 수행하도록 구현
ex) 단계 쪼개기 전략이 적용된 사례 - 컴파일러
- 컴파일러는 어떤 코드를 입력받아서, 실행 가능한 형태로 변환하는데, 컴파일러 작업은 여러 단계가 순차적으로 연결된 형태로 분리되어 있음
- 단계 쪼개기: 코드(텍스트)를 토큰화 -> 토큰 파싱 -> 구문 트리 생성 -> object code 생성
- 장점: 각 단계는 자신만의 문제에 집중하기 때문에 나머지 단계에 관해서는 자세히 몰라도 이해하기가 가능
ex) 상품의 결제 금액과 배송비를 구하는 코드
- 관련 모델 (0, 1, 2, 3으로 할당된 값들은 편한 )
struct Product {
let basePrice: Double
let discountRate: Double
let discountThreshold: Double
}
struct ShippingMethod {
let discountThreshold: Double
let discountRate: Double
let discountFee: Double
let feePerCase: Double
}
- 계산하는 코드
- 문제점: 계산이 두 단계로 구성 (상품 가격, 배송비)
- 추후에 상품 가격과 배송비 계산을 더 복잡하게 만드는 변경이 생기면 더욱 복잡해질 수 있으므로 코드를 두 단계로 나눌 것
func priceOrder(product: Product, quantity: Double, shippingMethod: ShippingMethod) -> Double {
let basePrice = product.basePrice * quantity
let discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate
let shippingPerCase = (basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountFee : shippingMethod.feePerCase
let shippingCost = quantity * shippingPerCase
let price = basePrice - discount + shippingCost
return price
}
- 리펙토링 - 별도의 함수를 하나 만들어서 단계 나누기
- 새로 생긴 문제점: 매개변수가 너무 많아서 읽기가 불편한점
func priceOrder(product: Product, quantity: Double, shippingMethod: ShippingMethod) -> Double {
let basePrice = product.basePrice * quantity
let discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate
let price = applyShipping(basePrice: basePrice, shippingMethod: shippingMethod, quantity: quantity, discount: discount)
return price
}
// 배송비 적용 코드
func applyShipping(basePrice: Double, shippingMethod: ShippingMethod, quantity: Double, discount: Double) -> Double {
let shippingPerCase = (basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountFee : shippingMethod.feePerCase
let shippingCost = quantity * shippingPerCase
let price = basePrice - discount + shippingCost
return price
}
- 리펙토링 - PriceData라는것을 하나 만들어서 주고받을 중간 데이터 구조를 만들것
- 장점: 매개변수를 PriceData를 만들어서 price에 관한 정보를 응집화하도록 구현
struct PriceData {
let basePrice: Double
let quantity: Double
let discount: Double
}
func priceOrder(product: Product, quantity: Double, shippingMethod: ShippingMethod) -> Double {
let basePrice = product.basePrice * quantity
let discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate
let priceData = PriceData(basePrice: basePrice, quantity: quantity, discount: discount)
let price = applyShipping(priceData: priceData, shippingMethod: shippingMethod)
return price
}
func applyShipping(priceData: PriceData, shippingMethod: ShippingMethod) -> Double {
let shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountFee : shippingMethod.feePerCase
let shippingCost = priceData.quantity * shippingPerCase
let price = priceData.basePrice - priceData.discount + shippingCost
return price
}
- 현재 priceOrder 함수를 보면 아직 단계가 완벽히 나누어진게 아니므로 리펙토링이 더 필요
- price와 연관된 코드들을 별도 함수로 빼면 더욱 상품 가격과 배송비에 관한 코드 분리가 가능
func priceOrder(product: Product, quantity: Double, shippingMethod: ShippingMethod) -> Double {
// 첫 번째 단계
let priceData = calculatePricingData(product: product, quantity: quantity)
// 두 번째 단계
let price = applyShipping(priceData: priceData, shippingMethod: shippingMethod)
return price
}
// 첫 번째 단계를 처리하는 함수
func calculatePricingData(product: Product, quantity: Double) -> PriceData {
let basePrice = product.basePrice * quantity
let discount = max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate
let priceData = PriceData(basePrice: basePrice, quantity: quantity, discount: discount)
return priceData
}
// 두 번째 단계를 처리하는 함수
func applyShipping(priceData: PriceData, shippingMethod: ShippingMethod) -> Double {
let shippingPerCase = (priceData.basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountFee : shippingMethod.feePerCase
let shippingCost = priceData.quantity * shippingPerCase
let price = priceData.basePrice - priceData.discount + shippingCost
return price
}
단계 쪼개기 정리
- 코드를 수정할 때 두 대상을 동시에 생각할 필요 없이 하나에만 집중할 수 있게끔하기위해 단계 쪼개기
- 로직을 단순히 쪼개기 전에, 입력받는것을 처리하기 쉬운 형태로 변경이 필요 (위에서 알아본 PriceData)
* 전체 코드: https://github.com/JK0369/ExRefactoring_5-4
* 참고
- Refactoring (Marting Flowler)
'Refactoring (리펙토링)' 카테고리의 다른 글
[Refactoring] 6-2. 캡슐화 (클래스 추출하기) (0) | 2023.03.22 |
---|---|
[Refactoring] 6-1. 캡슐화 (레코드 캡슐화하기) (2) | 2023.03.21 |
[Refactoring] 5-3. 기본적인 리펙토링 (여러 함수를 하나의 함수로 묶기) (0) | 2023.03.19 |
[Refactoring] 5-2. 기본적인 리펙토링 (변수 추출하기, 변수 인라인하기) (0) | 2023.03.18 |
[Refactoring] 5-1. 기본적인 리펙토링 (함수 추출하기, 함수 인라인하기) (0) | 2023.03.07 |
Comments