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
- 애니메이션
- 스위프트
- uiscrollview
- Refactoring
- collectionview
- MVVM
- ribs
- UITextView
- RxCocoa
- Protocol
- map
- Clean Code
- 리펙토링
- Xcode
- 리팩토링
- Observable
- HIG
- combine
- ios
- SWIFT
- clean architecture
- Human interface guide
- swiftUI
- 클린 코드
- tableView
- UICollectionView
- 리펙터링
- rxswift
- swift documentation
- uitableview
Archives
- Today
- Total
김종권의 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)
'Refactoring (리펙토링)' 카테고리의 다른 글
[Refactoring] 7-3. 기능 이동 (함수 옮기기, 중첩함수 제거, 모듈성) - 문장을 함수로 옮기기 (0) | 2023.04.18 |
---|---|
[Refactoring] 7-2. 기능 이동 (함수 옮기기, 중첩함수 제거, 모듈성) - 필드 옮기기 (1) | 2023.04.17 |
[Refactoring] 6-3. 캡슐화 (위임 숨기기) (0) | 2023.03.25 |
[Refactoring] 6-2. 캡슐화 (클래스 추출하기) (0) | 2023.03.22 |
[Refactoring] 6-1. 캡슐화 (레코드 캡슐화하기) (2) | 2023.03.21 |
Comments