리팩토링
차이점 일치시키기
- 비슷해 보이는 두 코드 조각을 합치려면 어떻게 해야 할까?
- 두 코드가 단계적으로 닮아가게끔 수정한다.
- 하위클래스의 내용이 비어 있으려면 메서드의 내용이 상위 클래스의 메서드 내용과 동일하면된다.
- 하나씩 하위클래스의 내용을 비우고 모두 비게 되면 하위 클래스에 대한 참조를 상위 클래스로 바꾼다.
변화 격리하기
- 객체나 메서드의 일부만 바꾸려면 어떻게 해야 할가?
- 변화를 격리하기 위한 바업
데이터 이주시키기
- 표현양식을 변경하려면 어떻게 해야 할까?
- 내부의 표현 양식을 변경한 후 외부 인터페이스를 변화시키는 방법
- 새로운 포맷의 인스턴스 변수를 추가한다.
- 기존 포맷의 인스턴스 변수를 세팅하는 모든 부분에서 새로운 인스턴스 변수도 세팅하게 만든다.
- 기존 변수를 사용하는 모든 곳에서 새변수를 사용하게 만든다.
- 기존 포맷을 제거한다.
- 새포멧에 맞게 외부 인터페이스를 변경한다.
메서드 추출
- 길고 복잡한 메서드를 읽기 쉽게 만들려면 어떻게 할까?
- 긴메서드의 일부분을 메서드로 분리해내고 이를 호출하게 한다.
메서드 인라인
- 너무 꼬여있거나 산재한 제어 흐름을 단순화하려면 어떻게 할까?
- 메서드를 호출하는 부분을 호출될 메서드의 본문으로 교체한다.
- 방법
- 메서드를 복사한다.
- 메서드를 호출하는 부분을 지우고 복사한 코드를 붙인다.
- 모든 형식 매개변수를 실제 매개 변수로 변경한다.
인터페이스 추출
- 메서드에 대한 두번째 구현을 추가하려면 어떻게 해야 할가?
- 공통되는 메서드를 담고있는 인터페이스를 만들면된다.
- 방법
- 인터페이스를 선언한다.
- 기존 클래스가 인터페이스를 구현하도록 만든다.
- 필요한 메서드를 인터페이스에 추가한다.
- 필요한 메서드를 인터페이스에 추가한다.
- 가능한 모든 곳의 타입 선언부에 클래스이름 대신 인터페이스 이름을 사용하게 바꾼다.
메서드 옮기기
- 메서드를 원래 있어야 할 장소로 옮기려면 어떻게 해야 할까?
- 어울리는 클래스에 메서드를 추가해주고, 그것을 호출하게 하자
- 방법
- 메서드를 복사한다.
- 원하는 클래스에 붙이고 이름을 적절히 지어준 다음 컴파일 한다.
- 원래 객체가 메서드 내부에서 참조된다면, 원래 객체를 새 메서드의 매개변수로 추가한다.
- 원래 메서드의 본체를 지우고, 그곳에 새 메서드를 호출하는 코드를 넣는다.
- 다른 객체에 두개 이상의 메시지를 보내는 코드를 볼 때마다 메서드 옮기기를 해주면된다
메서드 객체
- 여러개의 매개 변수와 지역변수를 갖는 복잡한 메서드를 어떻게 표현할까?
- 메서드를 꺼내서 객체로 만들자.
- 메서드 객체는 메서드 추출하기를 적용할 수 없는 코드를 간결하게 만들기 위한 용도로도 적합하다.
- 방법
- 메서드와 같은 매개변수를 갖는 객체를 만든다.
- 메서드의 지역변수를 개체의 인스턴스 변수로 만든다.
- 원래 메서드와 동일한 내용을 갖는 run() 이란는 이름의 메서드를 만든다.
- 원래 메서드에서는 새로 만들어진 클래스의 인스턴스를 생성하고 run()을 호출한다.
메서드 매개변수를 생성자 매개변수로 바꾸기
- 동일한 매개 변수를 같은 객체의 서로 다른 몇몇 메서드로 전달하는 경우, 매개변수를 한번만 전달 하게끔 API를 단순화할 수 있다.
- 생성자에 매개변수를 추가
- 매개변수와 같은 이름을 갖는 인스턴스 변수를 추가
- 생성자에서 인스턴수 변수의 값을 설정한다.
- ‘parameter’를 this.parameter로 하나씩 찾아 바꾼다.
- 매개변수에 대한 참조가 더 이상 존재하지 않으면 해당 매개변수를 메서드와 모든 호출자에서 제거한다.
- 이제 필요 없어진 this.를 제거한다.
- 변수명을 적적히 변경한다.
TDD 마스터하기
단계가 얼마나 커야하나?
- 각 테스트가 다뤄야 할 범위는 얼마나 넓은가?
- 리팩토링을 하면서 얼마나 많은 중간단계를 거쳐야 하는가?
테스트할 필요가 없는 것은 무엇인가?
- 테스팅해야할 목록
- 다른사람이 만든 코드를 테스트하지는 마라.
좋은 테스트를 갖췄는지 여부를 어떻게 알 수 있는가?
- 설계문제가 있음을 알려주는 테스트의 속성
- 긴 셋업 코드:
- 하나의 단순한 단언을 수행하기 위해 수백 줄의 객체 생성코드가 필요하다면 문제가 있는것이다.
- 객체가 너무 크다는 뜻이므로 나눌 필요가 있다.
- 셋업중복
- 공통의 셋업 코드를 넣어 둘 공통의 장소를 찾기 힘들다면, 서로 밀접하게 엉킨 객체들이 너무 많다는 뜻이다.
- 실행시간이 오래걸리는 테스트
- 깨지기 쉬운테스트
- 예상치 못하게 실패하는 테스트가 있다면 어느 애플리케이션의 특정 부분이 다른 부분에 이상한 방법으로 영향을 끼친다는 뜻이다.
- 연결을 끊거나 두 부분을 합하는것을 통해 멀리 떨어진 것의 영향력이 없어지도록 설계해야 한다.
피드백이 얼마나 필요한가?
- 테스트를 얼마나 작성해야 하나?
- 만약 어떤 구현에 대한 지식이 신뢰할 만 하다면 그에 대한 테스트는 작성하지 않아도 된다.
테스트를 지워야 할때는 언제인가?
- 테스트를 삭제할 경우 자신감이 줄어드는 것 같으면 절대 테스트를 지우지 말아야한다.
- 두 개의 테스트가 코드의 동일한 부분을 실행하더라도, 이 둘이 서로 다른 시나리오를 말한다면 그대로 남겨두어야한다.
거대한 시스템을 개발할 때에도 TDD를 할 수 있는가?
- 시스템에 있는 기능의 양은 TDD의 효율에 영향을 미치지 않는다.
프로젝트 중반에 TDD 도입하려면 어떻게 해야할까?
- 변경의 범위를 제한하고, 시스템에서 극적으로 단순화될 수 있지만 지금 당장 변할 필요가 없는 부분을 봤다면 그냥 그대로 놔두자.
- 테스트와 리팩토링 사이에 존재하는 교착상태를 푼다.
- TDD는 더 깔금한 설계를 할 수 있도록, 그리고 더 많은 것을 배워감에 따라 설계를 더 개선할 수 있도록, 적절한 때 적절한 문제에 집중할 수 있게끔 도와준다.
- TDD는 훨씬더 적은 수의 결함과 훨씬 더 깨끗한 설계의 코드를 작성하게 해준다.
- TDD는 시간이 지남에 따라 코드에 대한 자신감을 점점 더 쌓아가 수 있게 해준다.
- 테스트가 쌓여감에 따라 시스템의 행위에 대한 자신감을 더 많이 얻게 된다.
- 설계를 개선해 나감에 따라 점점 더 많은 설계 변경이 가능해진다.
TDD는 초기조건에 민감한가?
- 저자는 완벽하게 사리에 맞는 설계 아이디어가 결국은 틀린 것으로 판명날때가 종종 있는데, 그냥 시스템이 무슨일을할지 생각하고 나중에 설계가 알아서 정해지도록하는 것이 더 낫다는 판단을하였다.
- TDD는 스트레스를 훨씬 적게 받게 만든다.
- 모든 것에 대해 한꺼번에 걱정할 필요가 없고 지금 직면한 테스트 하나를 실행하게끔만 하면된다. 그뒤에 나머지를 전부 실행하면된다.
- TDD는 미지에 대한 두려움을 지루함으로 바꿔주는 효과가 있다.
- TDD는 설계 결정에 대한 피드백 고리를 단축시킨다
- 설계결정에 대한 피드백 루프의 길이는 설계에 대한 생각(API가 이런식으로 생기면 좋겠다)와 그에 대한 첫번째 예제(그 생각을 담고 있는 테스트 작성) 사이의 간격이다.
- 설계 아이디어를 몇 분사이에 그럴듯한 인터페이스로 전환하기만 함녀 피드백을 받을 수 있게 된다.
- TDD는 테스트 기술이 아니라 분석기술이며, 설계기술이다. 또한 개발의 모든 활동을 구조화 하는 기술이다.
- 단순 설계: 테스트를 통과하기 위해 필요한 만큼만 코딩하고 모든 중복을 제거한다면, 자동으로 현재까지의 요구사항에 딱 들어 맞는 설계를 얻게 될 것이고, 이 설계는 앞으로의 어떠한 사용자 스토리에 대해서도 동등하게 준비된 상태일 것이다.
- 리팩토링: 테스트가 있다면 큰 리팩토링을 수행하더라도 시스템의 행위가 변하지 않았다는 자신감을 얻을 수 있게 된다. 자신감이 커질수록 더 큰 리팩토링을 공격적으로 수행할 수 있게 되고, 결과적으로 시스템 수명이 더 길어질 것이다. 또한 리팩토링을 통해 다음 단계의 테스트를 더 쉽게 작성할 수 있다.
- CI/CD : 테스트는 좀더 자주 통합할 수 있게 해준다. 그리고 고객을 혼란 시키지 않으면서도 훨씬 더 자주 코드를 출시할 수 있게 된다.
다락의 주장
- GUI 에 대한 자동화 테스트는 만들 수 없다 ex) JSP, Servlet
- 분산객체에 대한 자동화 테스트는 만들 수 없다.
- 데이터 베이스 스키마를 개발할 땐 TDD를 적용할 수 없다. ex) JDBC
- 외부도구가 생성한 코드나 서드파티 코드를 테스트할 필요는 없다.
- 언어 컴파일러나 인터프리터등은 TDD로 만들어 낼수 없다.
TDD 수련법
- 간단하고 쉬운문제들을 TDD로 시도한다.
- 초록막대 주기는 가능하면 짧도록 한다.
- 초록 막대 주기의 최대시간을 정해놓고 진행하다가 시간을 초과하면 직전 초록막대 상태로 돌린다음 새로 시작한다.
- 진짜로 만들기 전까지만 가짜로 구현하기를 적극적으로 사용하려고 노력한다.
- 가짜로 구현하기는 초록 막대 주기가 짧아지는 가장 간단하고 빠른 방법이다.
- 같은 문제를 여러번 풀어본다.
- 초기에는 리팩토링 툴을 사용하지 않는 것이 좋다. 순서와 과정을 성큼 건너뛰게 되기 때문
- 여유를 가지고 겸손하게 속도를 조절하며 자신이 하는것을 관찰하고 기