드디어 리팩토링 기법들이 소개됩니다.
기본적인 리팩토링 챕터는 생각보다 길어서 다 읽는데 시간이 꽤 들었습니다.
글이 아니라 영상으로 마틴 파울러의 리팩토링 과정을 볼 수 있었다면 보다 흥미롭게 배울 수 있었을 것 같습니다.
(반대로 생각해보면 내가 이 과정을 영상으로 재구성해보는 것도 재밌겠다...)
이번에는 요약보다 궁금증, 기억할 부분 위주로 기록해보겠습니다.
6.1 함수 추출하기
함수 추출하기는 내가 가장 만힝 사용하는 리팩터링 중 하나다 (여기서 '함수function'라고 표현했는데 객체 지향 언어의 메서드method나 절차형 언어의 프로시저procedure/서브루틴subroutine에도 똑같이 적용된다.)
p.159
솔직히 function/method 개념 정의를 못하겠습니다... 그래서 개념정의를 찾아봤습니다.
Zedd 님의 깔끔한 설명을 가져왔습니다.
procedure/subroutine은 다음에 찾아보는걸로...
'목적과 구현을 분리'
(...중략...)
코드의 목적(강조)과 구현(반전) 사이의 차이가 그만큼 컸기 때문이다.
이 예제는 터미널 프로그램에서 UI 강조를 하는 코드인데 강조를 위해서 흑백 반전을 합니다. 흑백 반전이니까 구현 코드는 reverse() 그러나 UI를 강조한다는 의미를 살리기 위해서 highlight() 라는 이름으로 reverse()를 간접호출 해줬습니다. 재밌는 사례라고 생각했는데 내부 작동원리를 외부에 드러내는 것 보다 코드를 호출하는 클라이언트 입장에서 철저히 생각하면 이렇게 코드를 작성할 수도 있다는 것을 알게되었습니다.
6.3 변수 추출하기
객체는 특정 로직과 데이터를 외부와 공유하려 할 때 공유할 저옵를 설명해주는 적당한 크기의 문맥이 되어준다.
p.177
변수를 객체로 담아서 적당한 문맥을 제공하면 이해하기 쉬운 코드로 리팩토링 할 수 있습니다.
6.5 함수 선언 바꾸기
좋은 이름을 떠올리는 데 효과적인 방법이 하나 있다. 바로 주석을 이용해 함수의 목적을 설명해보는 것이다. 그러다 보면 주석이 멋진 이름으로 바뀌어 되돌아올 때가 있다.
p.180
주석 내용을 살려서 함수 이름을 더 정확하게 지을 수도 있습니다.
6.6 변수 캡슐화하기
먼저 그 데이터로의 접근을 독점하는 함수를 만드는 식으로 캡슐화
p.188
데이터의 유효범위가 넓을수록 캡슐화해야 한다.
p.189
// p.192
let defaultOwnerData = {firstName: "마틴", lastName: "파울러"};
export function defaultOwner() {return new Person(defaultOwnerData);} // 새 객체 반환
export function setDefaultOwner() {defaultOwnerData = arg;}
defaultOwnerData가 외부에서 변경되지 않기 위해서 defaultOwner()에서 새 객체로 deep copy해서 반환하고 있습니다. 외부의 영향을 차단하기 위해서 새 객체로 반환하는 것이 유용할 때가 있습니다. Swift에서는 struct를 사용하면 값 복사가 되기 때문에 이런 경우에 유용하게 사용할 수 수 있습니다.
6.8 매개변수 객체 만들기
데이터 항목 여러개가 이 함수에서 저 함수로 함께 몰려다니는 경우를 자주 본다. 나는 이런 데이터 무리를 발견하면 데이터 구조 하나로 모아주곤 한다.
p.197
이와 같은 범위(range)라는 개념은 객체 하나로 묶어 표현하는 게 나은 대표적인 예다.
p.199
데이터가 min, max로 나눠져있고 각각 데이터를 가져오는 경로가 다르면 생각이 분산됩니다. 그럴 때 객체로 묶어서 표현하면 생각의 범위를 줄여주고 단순히 데이터만 몰아둔게 아니라 객체 스스로 고유한 역할을 하게 만들 수 있습니다.
여기서는 기본 자바스크립트 객체가 아닌 클래스로 선언했는데,
자바스크립트에 대한 이해가 없어서 지인에게 문의해봤습니다.
{temp: 47, time: "2016-11-10 09:10"} 이렇게 json 형태로 작성하면 기본 자바스크립트 객체라고 합니다;;
그렇다면 이걸 객체로 보는게 맞겠네요...
앞서 챕터 4 테스트에 관한 예제를 Swift로 포팅했을 때 이런 기본 자바스크립트 객체 형태를 dictionary로 포팅했었는데, 그럴 필요 없이 구체적인 객체로 구현하는게 의도를 좀 더 살린 방식이었을 것 같습니다.
6.9 여러 함수를 클래스로 묶기
최상위 함수로 두면 못 보고 지나치기 쉽다는 문제가 있다.
p.204
이것도 별로 생각해보지 못한 문제인데, 최상위 함수는 코드 베이스 어디서든 호출될 수 있어서 관리하기 어렵다는 생각만 있었습니다.
그런데 클래스 아래에 묶이지 않는 전역 함수는 맥락을 파악하기 어려워서 해당 함수는 잊혀지기 쉽다는 것을 알게되었습니다.
6.10 여러 함수를 변환 함수로 묶기
여기서 주의할 점이 있다. enrichReading()처럼 정보를 추가해 반환할 때 원본 측정값 레코드는 변경하지 않아야 한다는 것이다. 따라서 이를 확인하는 테스트를 작성해두는 것이 좋다.
p.201
원본 데이터가 변경되지 않아야 하는 경우가 있으면 테스트 코드로 검증해두는 것이 좋습니다.
6.11 단계 쪼개기
각 단계는 자신만의 문제에 집중하기 때문에 나머지 단계에 관해서는 자세히 몰라도 이해할 수 있다.
p.215
한번에 고민해야하는 문제의 범위를 줄이는 방향으로 리팩토링
테스트가 느리거나 불편하면 리팩터링 속도가 느려지고 오류가 생길 가능성도 커진다.
p.222
테스트하기 쉬운 형태로 먼저 리팩토링
책 읽다가 발견한 것
저자는 리팩토링 절차를 아주 잘게 나눠서 진행합니다. 저 정도는 한 덩어리로 진행해도 될 것 같은것도 조심스럽게 하나씩 변경하고 있습니다. 이러면 실수할 확률을 줄 일수 있고 문제가 발생하는 범위를 줄일 수 있는 장점이 있다는 생각이 들었습니다. 한번에 많은 변경을 하지말고 최소 단위로 변경을 만들어봐야겠다는 생각이 들었습니다.
'CS > Refactoring' 카테고리의 다른 글
[Refactoring] Chapter 8: 기능이동 (0) | 2021.05.16 |
---|---|
[Refactoring] Chapter 7: 캡슐화 (0) | 2021.05.03 |
[Refactoring] Chapter 3, 4 과제 (0) | 2021.04.17 |
[Refactoring] Chapter4를 Swift로 따라해보기 (0) | 2021.04.17 |
[Refactoring] Chapter 4. 테스트 구축하기 (0) | 2021.04.17 |