12. 대규모 리팩토링 (Big Refactoring)
■ 게임의 본질
- 대규모 리팩토링을 할 때 어떤 것을 보게 될지 정확히 알 수 없고, 무엇을 해야 하는지에 대해서 정확히 말을 할 수가 없다.
- 대규모 리팩토링에서는 시간이 많이 걸리기 때문에 즉각적인 만족을 얻을수 없다.
- 어떤 필요에 의해서 나아가야 한다. (기능을 추가하거나 버그를 수정해야 할 때 리팩토링을 한다.)
- 프로그래밍 팀 전체의 동의가 필요하다.
- 아주 많은 변경을 위해 방향을 설정해야 하고, 팀 전체가 대규모 리팩토링이 진행 중이라는 사실을 인지하고 작업을 적절히 맞추어야 한다.
■ 왜 대규모 리팩토링이 중요한가?
- 리팩토링을 하지 않았을 때는 할 수 없던 어떤 무언가를 할 수 있다.
■ 네 가지 대규모 리팩토링
- Tease Apart Inheritance : 여러 가지 변화를 혼란스러운 방법으로 결합한 것 같은 엉켜있는 상속 구조를 다룬다.
- Convert Procedural Desing to Ojebct : 절차적 코드를 어떻게 해야 할지에 대한 고전적인 문제를 푸는데 도움을 준다.
- Separate Domain from Presentation : 사용자 인턴페이지와 데이터베이스에 대한 고전적인 2-티어 접근 방법으로 작성된 코드를 본다.
- Extract Hierarhy : 지나치게 복잡한 클래스를 서브클래스의 그룹으로 바꾸어 단순화 시킨다.
1. Tease Apart Inheritance
두가지 작업을 한번에 처리하는 상속 구조가 있는 경우
두개의 상속 구조를 만들고 하나가 다른 하나를 호출하도록 위임(delegation)을 사용하라.

■동기
- 상속은 잘못 사용되기 쉽고, 이런 오용은 서서히 진행된다.
- 하나의 상속 구조에서 두 가지 작업을 하는 경우는 쉽게 찾아낼 수 있다.
■절차
- 상속 구조에서 처리하고 있는 서로 다른 작업을 확인한다.
- 어떤 작업이 더 중요한지, 어떤 작업을 현재의 상속 구조에 남기고 어떤 작업을 다른 상속 구조로 옮길지 결정한다.
- 공통의 수퍼클래스에 Extract Class(6장)를 사용하여 보조 작업을 위한 객체를 만들고 이 객체를 보관하기 위한 인스턴스 변수를 만든다.
- 원래의 상속 구조에 있던 각각의 서브클래스에 대해서, 추출된 객체를 상속하여 서브클래스를 만들고, 전 단계에서 만든 인스턴스 변수들 이 서브클래스의 인스턴스로 초기화 한다.
- 각각의 서브 클래스에 대해 Move Method(7장)를 사용하여 추출된 객체의 서브클래스로 기능을 옮긴다.
- 서브클래스에 더 이상 크드가 없으면, 서브클래스를 제거한다.
- 모든 서브클래스가 제거될 때까지 계속한다. 새로 만든 상속 구조에 Pull Up Method(370)나 Pull Up Field(11장)등과 같은 리팩토링을 더 적용할 수 있는지 살펴본다.
■예제
- Deal이 원래 하나의 거래(deal)만을 표시
- ActiveDeal의 서브클래스 생성, 얼마후 PassiveDeal의 작은 서브클래스 생성
- 테이블 코드가 복잡해짐.
(해결방법)
1. 거래(deal)가 프레젠테이션 스타일보다 휠씬 중요하므로, Deal을 남겨두고 프리젠테이션 스타일을 별도 상속 구조로 뽑아낸다.
2. ExtractClass(179)로 프리젠테이션 스타일을 위한 클래스를 만든다.<그림12.2 참고>
3. 원래의 상속 구조에 있던 각각의 서브클래스에 대해서 추출된 객체를 상속하여 서브클래스를 만든다.<그림12.3 참고>
4. MoveMethod(170)와 MoveField(175)를 사용해 Deal의 서브클래스에 있는 프리젠테이션과 관계된 메소드와 변수를 PresentationStyle의 서브클래스로 옮긴다.<그림12.4 참고>
5. 두 작업이 분리 되었고, 각각에 대해 따로 작업하여 단순화 할 수 있다.<그림12.5 참고>
2. Convert Procedural Desing to Ojebct
절차적 스타일(procedural style)로 작성된 코드가 있으면,
데이터 레코드를 객체로 바꾸고, 동작을 쪼개서 객체로 옮겨라.

■동기
- ① 자바를 사용할 것
② 객체를 사용하지 말것. - 데이터가 거의 없는 클래스에 있는 긴 절차적(procedural)메소드와 단지 접근자만 가지고 있는 바보 같은 데이서객체
■절차
- 각각의 레코드 타입을 취해 접근자를 덤 데이터 객체(dumb data object)로 바꾼다.
- 모든 절차적 코드를 취해 하나의 클래스에 넣는다.
- 각각의 긴 프로시저를 취해 Extract Method(136)와, 다른 관계된 리팩토링을 적용하여 분해한다. 프로시저를 분해함에 따라 Move Method(170)르 사용하여 각각을 적절한 덤 데이터 클래스로 옮긴다.
- 원래의 클래스에서 모든 동작이 제거될 때까지 계속한다. 만약 원래의 클래스가 순수한 절차적 클래스였다면, 그 클래스를 제거하는 것이 무척 만족스러울 것이다.
■예제
- 1장에 있는 예제, Statement 메소드를 분해하여 기능을 여러 곳에서 흩어 놓는부분은 ConvertProcuduralDesign to Object의 필요성을 바로 보?는 좋은예이다.
3. Separate Domain from Presentation
도메인 로직을 포함하고 있는 GUI 클래스를 가지고 있다면,
도메인 로직을 분리하여 도메인 클래스를 만들어라.

■동기
- 클라이언트/서버의 2-티어 서비스 환경에서의 도메인 객체 분리
■절차
- 각각의 윈도우에 대한 도메인 클래스를 작성한다.
- 테이블을 가지고 있다면, 데이블의 행(ROW)을 표현하는 클래스를 만든다.
- 윈도우의 데이터를 검토한다.
- Extract Method(136)를 사용하여 프리젠테이션 로직에서 도메인 로직을 분리한다.
- 작업이 끝나면 프리젠테이션 클래스와 비즈니스 로직을 처리하는 도메인이 되고, 좀더 리팩토링을 한다.
■예제
너무 많은 작업을 하거나 또는 부분적으로라도 많은 조건문이 있는 클래스에 대해서는, 각각의 서브클래스가 특정 작업을 담당하도록 클래스의 실속 구조를 만들어라.

■동기
- 점진적 디자인에서 한 클래스가 하나의 아이디어를 구현하도록 했는데, 나중에 보니 실제로는 두세 개 또는 열 개의 아이디어를 구현하고 있다.
- 각각의 기능을 분리하는 것을 생각하는데 이 여러가지 기능을 분리시키는데는 전략이 필요하다. 조직 로직이 객체의 존속 기간동안 변하지 않고 남아있어야 한다.
- Extract Hierarhy 작업은 하루만에 끝날 수 있는 리팩토링이 아니다.
■절차
- 첫번째 절차(한번에 한단계씩 진행)
- 클래스가 처리하고 있는 각각의 작업을 확인한다
- 각각의 작업을 담당할 서브클래스를 만들고 원래의 클래스에 Replase Constructor width Factory Method를 사용한다.
- 한번에 하나씩, 조건 로직을 포함하는 메소드를 서브클래스로 복사하고, 의도된 기능에 맞도록 메소드를 손질한다.
- 수퍼클래스를 abstract로 선언할 수 있을 때까지 각각의 특별한 작업을 분리한다.
- 수퍼클래스에 있는 메소드가 모든 서브클래스에서 오버라이드 되었다면 메소드 몸체를 제거하고 abstract로 선언한다.
- 두번째 절차(처음부터 각각의 동작이 명확히 진행)
- 각각의 작업에 대한 서브클래스를 만든다
- Replace Construtor with Factory Method(350)를 사용하여 각각의 자업에 대해 적절한 서브클래스의 인스턴스르 리턴하도록 한다.
- 조건 로직을 포함하고 있는 메소드를 취해서 Replace Conditional with Polymor-phism을 적용한다.