Notice
Recent Posts
Recent Comments
Link
관리 메뉴

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

[Refactoring] 7-1. 기능 이동 (함수 옮기기, 중첩함수 제거, 모듈성) - 함수 옮기기 본문

Refactoring (리펙토링)

[Refactoring] 7-1. 기능 이동 (함수 옮기기, 중첩함수 제거, 모듈성) - 함수 옮기기

jake-kim 2023. 3. 26. 12:11

함수 옮기기

  • 어떤 함수가 자신이 속한 모듈 A의 요소들마다 다른 모듈 B의 요소들을 더 많이 참조하면 모듈 B로 옮겨줘야 좋은데, 이때 함수를 이동시큰 것
    • '모듈성'을 위해 이동
    • 모듈성이란? 프로그램의 어딘가를 수정하려 할 때 해당 기능과 깊이 관련된 작은 일부만 이해해도 가능하도록 해주는 것
    • 모듈성은 서로 연관된 요소들을 묶고, 요소의 연결관계를 쉽게 찾고 이해할 수 있도록 하는 것
    • 중첩된 함수가 어떻게 보면 은닉화라고 생각할 수 있지만, 중첩되면 그 안에서 숨겨진 데이터끼리 상호 의존하기가 아주 쉬운 구조가 쉬운 형태가 되므로 차라리 중첩하지 않고 바깥으로 빼내고 함수 내부에서는 parameter를 받아서 처리하게끔하는 구조로 구현할것

함수 옮기기

함수 옮기기 예제

ex) GPS 기록의 총 거리를 계산하는 trackSummary 함수 리펙토링하기

  • 함수를 리펙토링하는 과정도 중요
  • 아래 코드에서 중첩함수를 제거하는것이 목표
    • 중첩함수를 제거하는 이유: 숨겨진 데이터끼리 상호 의존하기가 아주 쉬운 구조가 되므로 중첩함수를 제거할 것
    • 중첩을 없애고 함수 내부에서는 parameter를 받아서 parameter에만 의존하도록 할 것

(각 함수 바디 부분은 예시의 편의를 위해 계산식은 임의로 구현)

struct Point {
    let lat, lng: Double
}

struct GPSInfo {
    let totalTime: Double
    let totalDistance: Double
    let pace: Double
}

func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = calculateDistance()
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    // 총 거리 계산
    func calculateDistance() -> Double {
        var result = 0.0
        for i in 1..<points.count {
            result += distance(p1: points[i-1], p2: points[i])
        }
        return result
    }
    
    // 두 지점 사이의 거리 계산
    func distance(p1: Point, p2: Point) -> Double {
        let radians = radians(degrees: 10)
        return radians * 3
    }
    
    // 라디안 값으로 변환
    func radians(degrees: Double) -> Double {
        3
    }
    
    // 총 시간 계산
    func calculateTime() -> Double {
        4
    }
}
  • radians와 calculateTime은 calculateDistance()에서만 사용하고 있으므로 calculateDistance()로 두 함수 이동
func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = calculateDistance()
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    // 총 거리 계산
    func calculateDistance() -> Double {
        var result = 0.0
        for i in 1..<points.count {
            result += distance(p1: points[i-1], p2: points[i])
        }
        return result
        
        // 두 지점 사이의 거리 계산
        func distance(p1: Point, p2: Point) -> Double {
            let radians = radians(degrees: 10)
            return radians * 3
        }
        
        // 라디안 값으로 변환
        func radians(degrees: Double) -> Double {
            3
        }
    }
    
    // 총 시간 계산
    func calculateTime() -> Double {
        4
    }
}
  • calculateDistance()를 바깥으로 빼내는 것이 목적이므로, 밖에 top_calculateDistance()를 선언하고, 바디 부분은 calculateDistance 내용 복붙
    • calculateDistance()에서는 top_calculateDistance()를 호출하도록 수정
    • 핵심은 계속 컴파일 에러가 나지 않고 코드가 동작하도록 리펙토링하는것
    • 베스트는 테스트 코드가 짜여져 있다면 매 단계마다 테스트 코드를 돌려보면서 잘 통과하는지 체크
func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = calculateDistance()
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    // 이 부분에서 top_calculateDistance(points:) 호출
    func calculateDistance() -> Double {
        return top_calculateDistance(points: points) // <-
    }
    
    func calculateTime() -> Double {
        4
    }
}

// 바깥으로 빼낸 calculateDistance 메소드
func top_calculateDistance(points: [Point]) -> Double {
    var result = 0.0
    for i in 1..<points.count {
        result += distance(p1: points[i-1], p2: points[i])
    }
    return result
    
    func distance(p1: Point, p2: Point) -> Double {
        let radians = radians(degrees: 10)
        return radians * 3
    }
    
    func radians(degrees: Double) -> Double {
        3
    }
}
  • 문제가 없으면 nested 되어있는 calculateDistance()를 삭제한 후 top_calculateDistance를 적용
func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = top_calculateDistance(points: points) // <-
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    func calculateTime() -> Double {
        4
    }
}
  • calculateDistance보다는 호출하는쪽에서 해당 함수 시그니처만 봐도 어떤 것인지 예측 가능하도록 하기위해서 calculateDistance가 아닌 totalDistance로 변경
// 이름 변경: top_calculateDistance -> totalDistance

func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = totalDistance(points: points)
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    func calculateTime() -> Double {
        4
    }
}

func totalDistance(points: [Point]) -> Double {
    var result = 0.0
    for i in 1..<points.count {
        result += distance(p1: points[i-1], p2: points[i])
    }
    return result
    
    func distance(p1: Point, p2: Point) -> Double {
        let radians = radians(degrees: 10)
        return radians * 3
    }
    
    func radians(degrees: Double) -> Double {
        3
    }
}
  • distance와 radians도 바깥으로 빼내기
func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = totalDistance(points: points)
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    func calculateTime() -> Double {
        4
    }
}

func totalDistance(points: [Point]) -> Double {
    var result = 0.0
    for i in 1..<points.count {
        result += distance(p1: points[i-1], p2: points[i])
    }
    return result
}

func distance(p1: Point, p2: Point) -> Double {
    let radians = radians(degrees: 10)
    return radians * 3
}

func radians(degrees: Double) -> Double {
    3
}

완성된 전체 코드)

struct Point {
    let lat, lng: Double
}

struct GPSInfo {
    let totalTime: Double
    let totalDistance: Double
    let pace: Double
}

func trackSummary(points: [Point]) -> GPSInfo {
    
    let totalTime = calculateTime()
    let totalDistance = totalDistance(points: points)
    let pace = totalTime / 60 / totalDistance
    
    return GPSInfo(totalTime: totalTime, totalDistance: totalDistance, pace: pace)
    
    // 가능하면 이 중첩 함수도 밖으로 뺄것
    func calculateTime() -> Double {
        4
    }
}

func totalDistance(points: [Point]) -> Double {
    var result = 0.0
    for i in 1..<points.count {
        result += distance(p1: points[i-1], p2: points[i])
    }
    return result
}

func distance(p1: Point, p2: Point) -> Double {
    let radians = radians(degrees: 10)
    return radians * 3
}

func radians(degrees: Double) -> Double {
    3
}

* 참고

- Refactoring (Marting Flowler)

Comments