관리 메뉴

김종권의 iOS 앱 개발 알아가기

[Refactoring] 5-2. 기본적인 리펙토링 (변수 추출하기, 변수 인라인하기) 본문

Refactoring (리펙토링)

[Refactoring] 5-2. 기본적인 리펙토링 (변수 추출하기, 변수 인라인하기)

jake-kim 2023. 3. 18. 01:31

리펙토링 핵심

  • 각 방법들을 '왜' 수행해야 하는지 깨닫고 유연하게 적용하기

변수 추출하기

  • 표현식이 너무 복잡한 경우, 따로 지역변수로 빼서 표현식을 쪼개어 관리하는 방법
  • 표현식에 이름을 붙이는 것
    • 반대 리펙토링: 변수 인라인하기

변수 추출하기

ex) 가격을 구하는 함수 getPrice(order:) 내부를 변수 추출하기 리펙토링

  • 초기상태
struct Order {
    let quantity: Double
    let price: Double
}

let order = Order(quantity: 100, price: 3000)
let price = getPrice(order: order)

private func getPrice(order: Order) -> Double {
    // 가격 = 기본가격 + 배송비 - 할인
    order.quantity * order.price // 기본 가격
        + min(order.quantity * order.price * 0.1, 100) // 배송비
        - max(0, order.quantity - 500) * order.price * 0.05 // 할인
}
  • 변수 추출하기를 통해 표현식에 이름을 붙이기
    • 변수 추출하기를 통해서 불필요한 주석도 삭제되고 가독성 높은 코드로 변경
private func getPrice(order: Order) -> Double {
    // 가격 = 기본가격 + 배송비 - 할인
    let basePrice = order.quantity * order.price
    let shipping = min(order.quantity * order.price * 0.1, 100)
    let discount = max(0, order.quantity - 500) * order.price * 0.05
    
    return basePrice + shipping - discount
}
  • 변수 추출하기의 장점
    • 복잡한 로직을 변수 추출하기하면 변수 이름으로 의미가 명확히 들어나면서 이해하기 쉽고 명확한 코드로 변경이 가능
    • 디버깅을 할때 break point걸때 따로 변수로 추출하는 부분을 걸수도 있기 때문에 디버깅에 용이
  • 변수 추출하기의 기준
    • 함수 안에서만 사용되는 표현식에 이름을 붙일 땐 그 이름이 들어갈 문맥을 살펴야 하기 때문에 함수 안에서만 의미가 있다면 변수로 추출하고, 함수 밖에서도 넓은 문맥의 의미를 가진다면 함수로 추출할것

변수 추출하기 + 객체화하기

  • 아래 getPrice는 Order와 연관되어 있으므로 Order안으로 이동시키면 여러곳에서 사용이 가능하고 더욱 객체화되어 기능의 응집도가 높아지는 장점이 존재

변경 전)

struct Order {
    let quantity: Double
    let price: Double
}

let order = Order(quantity: 100, price: 3000)
let price = getPrice(order: order)

private func getPrice(order: Order) -> Double {
    // 가격 = 기본가격 + 배송비 - 할인
    let basePrice = order.quantity * order.price
    let shipping = min(order.quantity * order.price * 0.1, 100)
    let discount = max(0, order.quantity - 500) * order.price * 0.05
    
    return basePrice + shipping - discount
}

변경 후)

struct Order {
    let quantity: Double
    let price: Double
    
    func getPrice() -> Double {
        // 가격 = 기본가격 + 배송비 - 할인
        let basePrice = quantity * price
        let shipping = min(quantity * price * 0.1, 100)
        let discount = max(0, quantity - 500) * price * 0.05
        
        return basePrice + shipping - discount
    }
}

let order = Order(quantity: 100, price: 3000)
order.getPrice()
  • basePrice, shipping, discount의 문맥을 생각해봤을때 외부에서도 접근해서 사용할 수 있으므로 따로 함수로 빼서 사용할것
    • 아래처럼 간단한 코드는 효과가 크진 않지만 덩치가 커질수록 공통 동작을 별도 이름으로 뽑아내서 추상화해놓으면 이 객체를 다룰때 매우 용이해지는 장점이 존재

(변수 추출하기 리펙토링 완성)

struct Order {
    let quantity: Double
    let price: Double
    
    func getPrice() -> Double {
        // 가격 = 기본가격 + 배송비 - 할인
        getBasePrice() + getShipping() - getDiscount()
    }
    
    func getBasePrice() -> Double {
        quantity * price
    }
    
    func getShipping() -> Double {
        min(quantity * price * 0.1, 100)
    }
    
    func getDiscount() -> Double {
        max(0, quantity - 500) * price * 0.05
    }
}

let order = Order(quantity: 100, price: 3000)
order.getPrice()

변수 인라인하기

  • 변수는 함수 안에서 표현식을 가리키는 이름으로 사용되어서 대체로 장점이 많지만, 추출한 이름이 원래 표현식과 별로 다를 바 없는 경우 오버 엔지니어링이 될 수 있고 주변 코드를 리펙토링하는 데 방해가 되므로 인라인할것
    • 반대 리펙토링: 변수 추출하기

ex) 변수 인라인하기 리펙토링

(적용 전)

let basePrice = order.getBasePrice()
if basePrice > 1000 {
    print("big")
}

(적용 후)

if order.getBasePrice() > 1000 {
    print("big")
}

* 참고

- Refactoring (Marting Flowler)

Comments