-
리펙터링 스터디 2주차 공부 정리TDD 2022. 7. 17. 13:13
- 기본적인 리팩터링
기본적으로 많이 사용한는 리팩터링은
- 함수 추출하기
- 변수 추출하기
이 두 리팩터링을 반대로 진행하는
- 함수 인라인하기
- 변수 인라인하기
추출은 이름짓기 , 코드의 이해도가 높아지면 이름을 바꿔야할때가 많음
- 함수 선언 바꾸기
- 변수 이름바꾸기
- 변수 캡술화 하기
- 매개변수 객체 만들기
함수의 구성과 이름 짓기는 가장 기본적인 저수준 리팩터링이다.
일단 함수로 만들고 나중에 고수준의 모듈로 묶어야함
- 여러함수를 클래스로 묶기
묶은 모듈을 작업처리 과정을 명확한 단계로
구분짓는 단계 쪼개기를 적용할 때도 많다.
- 단계 쪼개기
6.1 함수 추출하기
배경
코드 조각을 찾아 무슨일을 하는지 파악한 다음 , 독립된 함수로 추출하고 목적에 맞는 이름을 붙힌다.
기준
- 재사용성을 기준으로 할 수 있다.
- 내눈에는 목적과 구현을 분리하는 방식이 합리적인 기준으로 보임
- 함수는 짤때 대체로 몇줄만 담도록 작성하는 습관이 생김
- 오히려 코드 줄 수 가 길어지지만 코드의 목적 (강조)와 구현 (반전) 사이의 차이가 그만큼 컷기 때문이다.
- 함수를 짧게 만들면 함수의 호출이 많아져서 성능이 느려질까?
- 코드 덩어리 첫머리에 그 목적을 설명하는 주석이 달려있을때가 많다.
절차
- 함수를 새로 만들고 목적을 잘 드러내는 이름을 붙인다. (어떻게가 아닌 무엇을 하는지 드러나야한다.)
- 추출할 코드를 원본 함수에서 복사하여 새 함수에 붙여넣는다.
- 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 검사한다.
- 변수를 다 처리했다면 컴파일 한다.
- 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 바꾼다.(추출한 함수로 일을 위임)
- 테스트한다.
- 다른 코드에 방금 추출한것과 같거나 비슷한 코드를 살핀다. 있다면 추출함 함수를 호출하도록 바꿀지 검토한다.(인라인 코들 함수 호출로 바꾸기)
예사: 유효범위를 벗어나는 변수가 없을 때
A
그냥 뽑아내면 됨
예시: 지역 변수를 사용할 때
지역 변수와 관련하여 가장 간단한 경우는 변수를 사용 이 경우에는 지역변수들을 매개변수로 넘기면 된다.
다음 코드를 보자 .
예시: 지역 변수의 값을 변경할 때
지역 변수에 값을 대입하게 되면 문제가 복잡해짐
매개변수에 값을 대입하는 코드를 발견하면 그 변수를 쪼개서 임시변수를 새로 하나 만들어 그 변수에 대입하게 한다.
대입 대상이 되는 임시 변수는 크게 두 가지로 나눌 수 있다.
- 변수가 추출된 코드 안에서만 사용될 때
즉 이변수는 추출된 코드 안에서만 존재한다.
변수가 초기화되는 지점과 실제로 사용되는 지점이 떨어져있다면
문장슬라이드로 변수 조작을 한곳에서 처리하도록 모아두면 편하다.
이보다 특이한 경우
- 변수가 추출한 함수 밖에서 사용될때 이럴 때는 변수에 대입된 새 값을 반환해야한다.
앞 코드를 다시 살펴본다.
앞 예시에서 수행한 리팩터링들은 모두 간단해서 단번에 처리했지만
단계를 나눠서 진행해보자.
- 먼저 선언문을 변수가 사용되는 코드를 근처로 슬라이드한다.
- 그런 다음 추출할 부분을 새로운 함수로 복사한다.
- outstanding 의 선언문의 추출할 코드 앞으로 옮겪기 때문에 매개변수로 전달 하지 않아도 된다, 추출한 코드에서 값이 변경된 변수는 outstanding 뿐이다. 따라서 이값을 반환한다.
- 컴파일한다.
- 다음으로 넘어가서 추출한 코의 원래자리를 새로 뽑아낸 함수를 호출하는 문장으로 교체한다.
이때 원본 변수인 outstanding에 const를 붙여 불변으로 만들었다.
값을 반환할 변수가 여러 개라면 ?
- 주로 추출할 코드를 다르게 재구성하는 방향으로 처리한다.
함수가 값을 하나만 반환하는 방식을 선호하기때문에
각을 반환하는 함수를 여러 개 만든다.
- 레코드(객체)같은걸로 묶어서 반환해되 되지만
- 임시 변수 추출작업을 다른 방법으로 처리하는 것이 나을 때가 많음
- 여기서는 임시 변수를 질의 함수로 바꾸거나 , 변수를 쪼개는 식으로 처리하면 좋다.
사실 여기부터는 이해해지지 않는 아무말 대잔치
- 나는 단계를 작게 쪼개는걸 좋아하기 때문에 내 본능은 먼저 중첩 함수를 추출하고나서 새로운 문맥으로 옮기라고 말한다.
- 따라서 중첩 함수로 추출할 수 있더라도 최소한 원본 함수와 같은 수준의 문맥으로 먼저 추출해보자
- 그러면 코드를 제대로 추출햇는지 즉각 판별할 수 있다.
6.2 함수 인라인하기 (inline Function)
배경
이 책은 목적이 드러나는 짤막한 함수를 이용하기를 권한다.
그래야 코드가 명료하고 이해하기 쉬워지기 때문이다.
때로는 함수 본문이 이름만큼 명확한 경우도 있다.
또는
함수 본문 코드를 이름만큼 깔끔하게 리팩터링할 때도 있다.
이럴 때는 그 함수를 제거한다.
간접 호출은 유용할 수도 있지만 쓸데없는 간접호출은 거슬릴 뿐이다.
리팩터링 과정에서 잘못 추출된 함수들도 다시 인라인한다.
잘못 추출된 함수들을 원래 함수로 합친 다음 , 필요하면 원하는 형태로 다시 추출하는 것이다.
간접 호출을 너무 과하게 쓰는 코드도 인라인 대상 이다.
다른 함수로 단순히 위임하기만 하는 함수들이 너무 많아서 위임 관계가
복잡하면 인라인 해버린다.
절차
- 다형 메서드인지 확인한다.
- 인라인할 함수를 호출하는 곳을 모두 찾는다.
- 각 호출문을 함수 본문으로 교체한다.
- 하나 교체할 때마다 테스트한다.
- 함수 정의(원래 함수)를 삭제한다.
말로는 아주 간단해 보지이지만 실제로는 그렇지 않을 때가 많다.
재귀호출 , 반환문이 여러개인함수
상황히 복잡하다면 인라인하기를 적용하면 안된다.
예시
한줄씩 호출한곳으로 로직을 옮기는 작업을한다.
핵심은 잘게 나눠서 처리하는 것
테스트가 실패하면 최근 정상코드로 돌아온다음
단계를 잘게 나눠서 다시 리팩터링한다.
6.3 변수 추출하기 (Extract Variable)
배경
표현식이 너무 복잡해서 이해하기 어려울때
지역 변수를 활용하면 표현식을 쪼개 관리하기 더 쉽게 만들 수 있다.
이 과정에서 추가한 변수는 디버긴에 도움 디버깅에 도움된다.
중단점을 지정하거나 상태를 출력하는 문장을 추가할 수 있기 떼문이다.
변수 추출을 고려한다고 함은 표현식에 이름을 붙이고 싶다는 뜻이다.
이름을 붙이기로했다면
그 이름이 들어갈 문맥도 살펴야한다.
함수안에서만 의미가 있다면 그 함수안에서의 변수의 의미로 좋음
그러나 함수를 벗아난 문맥에서 의미가 된다면
이름이 통용되는 문맥을 넓히면 다른코드에서 사용할수 있기때문에
같은식을 중복해서 작성하지 않아도된다.
중복이 적으면서 의도가 잘드러나는 코드를 작성할 수 있다
문맥을 넓힐 때 생기는 단점은 할일이 늘어난다는 것
이밋 변수를 질의 함수로 바꾸기를 적용할때까지 놔둔다.
클래스 안의 코드를 다룰때는 함수 추출하기를 쉽게 적용할 수 있다.
절차
- 추출하려는 표현식에 부작용은 없는지 확인한다.
- 불변 변수를 하나 선언하고 이름을 붙일 표현식의 복제본을 대입한다.
- 원본 표현식을 새로 만든 변수로 교체한다.
- 테스트 한다.
- 표현식을 여러곳에서 사용한다면 하나교체할때마다 테스트한다.
예시
기본가격은 상품가격에 수량을 곱한값이다. 변수를 만들고 적절한 이름을 지어준후 교체한다.
더쓰이는 부분을 더교체한다.
수량할인도 추출하고 교체한다.
배송비도 똑같이 처리한다. 다 수정하면 주석은 지워도 된다.
추출하려는 이름을 같지만 주문을 표현하는 Order클래스 전체에 적용된다.
나는 변수가 아닌 메서드로 추출하는 편이다.
걍 계산하는 객체를 따로 빼버림
6.4 변수 인라인 하기
배경
변수는 함수안에 표현식을 가리키는 이름으로 쓰임 (예 기본급은, 수량*가격 = 기본급)
대체로 긍정적임
원래이름이랑 차이가 없을 때도 있음
이럴땐느 변수를 인라인하는 것이 좋다.
절차
- 대입문의 우변(표현식)에서 부작용이 생기지 않는지 확인
- 변수가 불변으로 선언되지 않았다면 불변으로 만든 후 테스트
- 이 변수를 가장 처음 사용하는 코드를 찾아서 대입문 우변의 코드를 바꾼다.
- 테스트 한다.
- 변수를 사용하는 부분을 모두 교체할 때까지 이 과정을 반복한다.
- 변수 선언문과 대입문을 지운다.
- 테스트한다.
6.5 함수 선언 바꾸기 (Change Function Declaration)
배경
함수는 프로그램을 작은 부분을 나누는 주된 수단.
잘 정의하면 새로운 부분을 추가하기가 쉬워지지만,
잘못 정의하면 연결부를 수정하기 어렵게 한다.
이러한 연결부에서 가장 중요한 요소는 함수의 이름이다.
함수의 구현코드를 살표볼 필요도 없이 호출문만 보고도 무슨일을하는 지 알 수 있다.
그냥 내비두고 싶은 유혹에 빠진다 -> 고작 이름인데?
이름이 잘못된 함수를 발견하면 더 나은 이름이 떠오르는 즉시 바꾸라는 명령으로 받아들인다.
그래야 나중에 그 코드를 다시 볼때 무슨일을 하는지 또 고민하지 않게 된다.
좋은 이름을 떠올리는데 효과적인 방법이 하나 있다.
주석을 이용해 목적을 설명해보는 것 , 그러다 보면 주석이 멋진 이름으로 바귀어 돌아올 때가 있다.
매개변수 올바르게 선택하기
만약에 지불기한이 넘었는지 판단하는 함수가 있다고 치자
매개변수는 지불 객체가 적절한지, 마감일이 적절한지
지불객체로 정하면 이 함수는 지불객체의 인터페이스와 결합되버린다.
대신 지불이 제공하는 여러 속성에서 쉽게 접근할 수 있어서
내부로직이 복잡해 지더라도 이함수를 호출하는 코드를 일일히 찾아서 변경할
필요가 없다.
실질적으로 함수의 캡슐화 수준이 높아지는 것
이 문제의 정답은 정답이 없다는 것
그에 맞게 코드를 개선할 수 있도록 함수 선언 바꾸기 리팩터링과 친숙 해져야만 한다.
단순히 이름만 바꿀때는 함수 이름 바꾸기라고 표현해서 확실히 구분할 것이다.
이름을 바꿀때는 매개변수를 변경할때든 절차는 똑같다.
절차
이책에서는 다른 리팩터링들은 절차를 한가지만 소개했다.
방법이 하나뿐이라서 대부분 상황에서 효과적인 방법이라서
근데 함수선언 바꾸기는 상황이 다름
간단한 절차
- 매개변수를 제거하려거든 먼저 함수 본문에서 제거 대상 매개변수를 참조하는 곳은 없는지 확인한다.
- 메서드 선언을 원하는 형태로 바꾼다.
- 기존 메서드 선언을 참조하는 부분을 모두 찾아서 바뀐 형태로 수정한다.
- 테스트한다.
변경할게 둘 이상이면 차라리 나눠서 처리하는 편이 나을 때가 많다.
따라서 이름 변경과 매개 변수 추가를 모두 하고 싶다면
각각을 독립적으로 처리하자
(그러다 문제가 생기면 작업을 되돌리고, 마이그레이션 절차라고 부른다.)
마이그레이션 절차
- 이어지는 추출 단계를 수월하게 만들어야 한다면 함수의 본문을 적절히 리팩터링한다.
- 함수 본문을 새로운 함수로 추출한다.
- 추출한 함수에 매개변수를 추가해야한다면 간단한 절차를 따라 추가한다.
- 테스트한다.
- 기존 함수를 인라인한다.
- 이름을 임시로 붙여 뒀다면 함수 선언 바꾸기를 한 번 더 적용해서 원래 이름으로 되돌린다.
- 테스트한다.
프록시 패턴으로 랩핑하고
함수를 수정하고 나중에 끝나면 다 바꿔준다.
근데 패턴과 다른점은 일단
패턴을 써서 테스트를하고
기존 함수들을 새함수를 쓰게 바꾼다는 거다.
그리고 새함수 이름을 기존 함수의 이름으로 바꾼다.
새함수에
기존함수를 인라인한다.
조금씩 새함수명으로 바꾼다.
근데 IDE에서 요즘은 한번에 다 바꿔줌
6.6 변수 캡슐화하기(Encapsulate Variable)
배경
리팩터링은 결국 프로그램의 요소를 조작하는 일이다.
함수를 사용한다는 건 대체로 호출한다는 뜻이고
함수의 이름을 바꾸거나 다른 모듈 로 옮기기는 어렵지 않다.
반대로 데이터는 데이터는 함수보다 다루가가 까다로운데,
데이터를 참조하는 모든 부분을 바꿔야 코드가 제대로 작동한다.
유효범위가 넓어질수록 다루기 어려워진다.
그래서 접근할 수 잇는 범위가 넓은 데이터를 옮길 때는 먼저 그 데이터로의 접근을 독점하는
함수를 만드는 식으로 캡슐화 하는 것이 가장 좋은 방법일 때가 많다.
데이터 재구성이라는 어려운 작업을 함수 재구성이라는 더 단순한 작업으로 변환하는 것이다.
'TDD' 카테고리의 다른 글
리팩터링 5주차 발표자료 (0) 2022.08.07 리팩터링 4주차 발표자료 (0) 2022.07.31 리펙터링 3주차 발표자료 (0) 2022.07.24 리펙터링 2주차 발표자료 (0) 2022.07.17