본문 바로가기

swift공부

[Swift] 리픽토링_메서드 정리 - 메서드 내용 직접 삽입, 메서드를 메서드 객체로 전환

메서드 내용 직접 삽입이란?

메서드 추출과는 반대로 메서드 기능이 너무 단순해서 메서드명만 봐도 너무 뻔할 경우, 그 메서드의 기능을 호출하는 메서드에 넣고 해당 메서드를 제거하는 방식이다. 

 

리펙토링의 핵심은 의도한 기능을 한눈에 파악할 수 있는 직관적인 메서드명 사용과 메서드를 간결하게 만드는 것이다. 하지만 메서드명에 모든 기능이 반영될 정도로 메서드 기능이 지나치게 단순할 때는 나누어진 메서드를 없애는 것이 오히려 효과적일 수도 있다. 

 

  func getRating() -> Int {
        return moreThanFiveLateDeliveries() ? 2 : 1
    }

    private func moreThanFiveLateDeliveries() -> Bool {
        return numberOfLateDeliveries > 5
    }

다음과 같이 rating을 얻는 코드에서 2 혹은 1을 얻는 경우는 numberOfLateDeliveries에만 달려있을 정도로 단순한 조건이다. 

 

   func getRating() -> Int {
        return numberOfLateDeliveries > 5 ? 2 : 1
    }

때문에 이렇게 메서드로 구분하지 않아도 쉽게 파악이 가능하다면 메서드 내용을 직접 삽입하는 것도 좋은 방법일 수 있다. 

 


메서드를 메서드 객체로 전환 기법을 적용하기 전에 해당 기법을 실시하면 좋다. 필요한 기능이 든 메서드의 각종 호출을 메서드 객체에 넣을 때 메서드와 그 메서드가 호출하는 온잦 메서드를 옮기는 것보다 메서드 하나만 옮기는 것이 더 편할 수 있다. 

 

메서드를 메서드 객체로 전환기법이란?

지역변수 때문에 메서드 추출을 적용할 수 없는 긴 메서드가 있을 땐 그 메서드 자체를 객체로 전환해서 모든 지역변수를 객체의 필드로 만드는 방법

 

지역변수가 많다면 메서드를 쪼개기 힘들어진다. 때문에 객체로 만들어 모든 지역변수가 메서드 객체의 속성으로 만드는 방법이 있다. 

 

class Account {
    let baseRate: Int
    let customerType: String

    init(baseRate: Int, customerType: String) {
        self.baseRate = baseRate
        self.customerType = customerType
    }

    // 계산 로직이 복잡하게 직접 작성됨
    func gamma(inputVal: Int, quantity: Int, yearToDate: Int) -> Int {
        let discount = customerType == "VIP" ? 20 : 10
        let base = inputVal * quantity
        let result = base - baseRate + yearToDate - discount
        return result
    }
}

gamma 메서드를 보면 baseRate와 customerType 두 개의 지역변수를 사용하고 있다. 이러한 지역변수가 메서드 분해를 어렵게 만든다. 또한 메서드 안에 많은 연산이 들어가 있어 한눈에 파악하기 어렵다. 

 

 

class Account {
    let baseRate: Int
    let customerType: String

    init(baseRate: Int, customerType: String) {
        self.baseRate = baseRate
        self.customerType = customerType
    }

    func gamma(inputVal: Int, quantity: Int, yearToDate: Int) -> Int {
        return Gamma(account: self, inputVal: inputVal, quantity: quantity, yearToDate: yearToDate).compute()
    }
}

// — Account에 의존함
class Gamma {
    private let account: Account
    private let inputVal: Int
    private let quantity: Int
    private let yearToDate: Int

    init(account: Account, inputVal: Int, quantity: Int, yearToDate: Int) {
        self.account = account
        self.inputVal = inputVal
        self.quantity = quantity
        self.yearToDate = yearToDate
    }

    func compute() -> Int {
        // Account에 의존하는 로직들
        let discount = account.customerType == "VIP" ? 20 : 10
        return inputVal * quantity - account.baseRate + yearToDate - discount
    }
}

 

이를 해결하기 위해 Account에 의존하는 Gamma라는 객체를 만들어 Account 에 gamma 메서드에 있던 기존 복잡했던 코드를 옮겨와서 해결한다. gamma 메서드 안에는 Gamma().compute()로 보여지기 때문에 의도를 쉽게 알아낼 수 있다.