서론
이 책을 접할 때 마침 개인적으로 ruby(정확히는 rails를 위한 ruby) 공부를 하고 있었고,
또한 리팩토링 책도 유명하긴 하지만 java 기반으로 알고 있었는데, 루비 기반으로 된 책이면 둘 다 접할 기회라 생각하여 읽게 되었다.
구성
크게 리팩토링이 무엇인지, 왜 쓰는지, 언제 써야 하는지 등 개요가 나오고 단위 테스트 잠깐 소개, 그 이후는 열거 방식으로 나열되어 있다.
현재 열거 부분 첫 챕터인 메서드 정리 부분까지 본 상태인데, 아직 초반이라 그런지 그 전부터 체득된 패턴들이 많이 나와서 익숙했다.
뒷 부분에는 클래스 추출 등 데이터 설계까지 고려하는 부분이 나오는 것 같은데 읽어봐야 알 것 같다.
메서드 챕터 부분은 메서드를 리팩토링하면서 접할 수 있는 모듈화, 메서드 내 파라미터 등을 어떻게 처리할지 등에 대한 사례들 있는데 사실 이런 과정들은 경험에 의해 체득됬었지, 이렇게 패턴별로 나열해서 생각해본적은 없었던 것 같다.
죽 보면서 이렇게 이 때는 이런게 필요하겠다 하는 식으로 정리해서 내 것으로 가지고 있으면 언젠간 도움이 될 거 같다.
ruby
oop, 동적 타입, 스크립트 언어 ( +함수형 프로그래밍 )
사실 ruby는 큰 기대를 안하고 rails 공부를 위해 접했는데, 신선한 면이 많은 언어였다.
특히 컬렉션을 다루는 enumerable 메서드들이 편리한게 많다.
arr = (1...4).select { |x| x % 2 == 0 } # => [2]
최근 애용하고 있는 lodash에도 이러한 컬렉션 메서드들이 꽤 있다.
그 외 매개변수가 아닌 코드 블럭 자체를 파라미터로 넘기는 등 접할 땐 당황스럽지만 편리한 기능들이 있었다.
리팩토링
Chap 7: 객체 간의 기능 이동
- Move Method
- 메서드가 자신이 속한 클래스보다 다른 클래스의 기능을 더 많이 이용할 경우 그 클래스로 메서드 이동
- Move FIeld
- 필드가 자신이 속한 클래스보다 다른 클래스에서 더 많이 사용될 경우 그 클래스로 필드 이동
- Extract Class <-> Inline Class
- 클래스 분리/합침
- Hide Delegate ( 대리 객체 은폐 ) <-> Remove Middle Man ( 과잉 중개자 제거 )
- 객체 내 메서드에서 내부 객체 메서드를 콜하는 상황일때 Forwardable 모듈을 이용 위임하라는 내용인데 루비 한정으로 보여짐
- 굳이 다른 언어에서 구현하려면 랩핑하는 메서드를 추가하는 정도
- Hide Delegate ( 대리 객체 은폐 ) <-> Remove Middle Man ( 과잉 중개자 제거 )
- 클래스 분리/합침
Chap 8: 데이터 체계화
- Self Encapsulate Field
- indirect/direct vailable access
- 필드를 직접 접근하지 말고 getter/setter 역할을 하는 메서드를 사용하라는 내용
- oop에서 당연히 필요한 요소로 생각했으나 반대의 입장도 있는 듯
- Replace Data Value with Object
- 한 필드가 단순 데이터에서 복잡한 데이터를 표현해야 할 경우 그 필드를 객체로 전환
- Change Value to Reference <-> Reference to Value
- Replace Array with Object
- row = [ Liverpool, 15 ] => row.name = "Liverpool"; row.wins = "15"
- 이런 식으로 다른 타입의 데이터들을 나열해서 넣는 케이스가 흔하지는 않은 것 같다.
- Replace Hash with Object
- Change Unidirectional Association to Bidirectional <-> Bi to Uni
- 두 클래스가 서로의 기능을 공유할때 참조/연결되게끔 메서드를 작성
- 혹은 반대로 종속성/버그발생 증가를 막기 위해 연결을 푸는 경우도 있다.
- Replace Magic Number with Symbolic Constant
- 특수한 의미가 있는 상수는 상수명을 명시해준다.
- Encapsulate Collection
- 컬렉션은 일반 데이터와 달리 반환할 때 사본을 반환하는 것이 좋다. 데이터 조작 가능성이 있기 때문에.
- 컬렉션을 바로 set 하는 대신 컬렉션 아이템을 add/remove하는 메서드를 작성
- Replace Type Code with Polymorphism/Module Extension/State/Strategy
- 타입 코드가 클래스의 기능에 영향을 미칠때 -> 메서드 내에서 조건 분기를 시키는 대신 module으로 재정의/확장해 사용
- Replace Subclass with Fields
- 상수 데이터만 반환하는 하위클래스들이 있을 경우 상위클래스 필드로 합침
- Person, Male, Female
- 이런 케이스도 설계가 매우 잘못되지 않는 이상 보기 힘든 케이스로 보임
- Lazily Initialized Attrtibute <-> Eagerly Initalized Attribute
- 초기화를 접근 시 해야 하는가? <-> 생성 시 해야 하는가?
- 접근 시 하는 경우
- 초기화해야할 필드가 많아져도 가독성 유지 가능
- 생성 시 하는 경우
- 초기화 로직이 생성자 안에 캡슐화됨
- 값 질의 시 일관된 결과 / 디버깅 시 문제 가능성 없음
Chap 9: 조건문 간결화
- Decompose Conditional
- 읽기에 복잡한 조건문은 메서드로 따로 뺌
- Recompose Conditional
- 삼항 연산자 대입문을 or을 이용해 표기
parameters = params ? params : []; parameters = params || [];
- 조건문을 명시적 return 문으로 교체
return 2 if days_rented > 2 1
- Consolidate Conditional Expression
- 조건문을 합침으로써 가독성 증가 및 메서드 추출의 가능성을 만들 수 있음
- Consolidate Duplicate Conditional Fregments
- 조건문의 모든 구간에 같은 코드가 있으면 밖으로 빼낸다. 당연한 내용.
- Remove Control Flag
# don't do this done = false until done do if ( condition ) # do something done = true end value -= 1 end
# instead of ... until done do if ( condition ) # do something return value end value -= 1 end
- done 같은 제어 플래그를 쓰는 대신 return 으로 빠져나가게끔 작성한다.
- 이러한 제어 플래그는 구조적 프로그래밍에서 사용했던 문법의 잔재
- Replace Nested Conditional with Guard Clauses
- if-then-else 구조는 읽을 때 if 절과 else 절의 중요성이 똑같다고 판단하게끔 한다.
- if-else 절이 복잡할때 특이한 case들은 검사( 감시절 : guard clause )하여 return 시킨다
- “이것은 드문 경우이니 이 경우가 발생한다면 작업을 수행한 후 빠져나가라”
- 현재 코드의 model 코드에 비슷한 코드
- Replace Conditional with Polymorphism
- 타입 등으로 다른 동작을 조건문을 통해 분기시킬 때 재정의(다형성)을 이용
- Null 검사를 널 객체에 위임
- 위와 비슷하게 null 검사도 조건문을 통해 검사하지 말고 클래스에 해당하는 널 클래스를 작성
- 필요성?
- Introduce Assertion
- 현재 코드에선 단위 테스트가 이 기능을 하고 있는 것 같다.
- 또한 단위 테스트가 실제 로직 코드 파일과 분리되어 있고 다양한 케이스를 추가할 수 있음
Chap 10: 메서드 호출 단순화
- Rename Method
- 네이밍. 당연해서 부가 설명 필요 없음
- Add Parameter <-> Remove Parameter
- 매개변수가 계속 길어지면 가독성이 안좋아지는 경우가 많다. 추가보단 정리를 권함
- 매개변수가 많을 시 객체로 묶을 수 있는 지 체크
- Separate Query from Modifier
- 상태 변경 등 액션 + 리턴을 동시에 하는 메서드가 있으면 둘을 분리
- 합쳐져 있을 경우 눈에 띄지 않는 부작용이 생길 수 있음
- ex 여러번 값 조회 테스트 시 내부 상태 값이 예상치 못하게 변하는 케이스
- 멀티스레딩 환경 개발 시에도 예외는 없다. 값 조회, 상태 변경 기능을 분리 작성 후, 제 3의 메서드에서 합쳐서 사용하면 문제도 안생기고, 모듈화된 두 메서드도 재사용 가능하다.
- Replace Parameter with Explicit Methods
- 매개변수에 따라 분기해 다른 코드를 실행할 경우엔 아예 메서드를 분리시켜 버리라는 내용
set_value("height", 10) , set_value("width", 50) -> height(10), width(50),
- 매개변수가 가변적이면 하지 않는게 좋다.
- Preserve Whole Object - 객체에서 가져온 여러 값을 파라미터로 전달하고 있는 경우, 아예 객체를 전달
- Replace Parameter with Method
- A메서드 결과를 B메서드에 파라미터로 전달한다. 근데 B 내에서도 A메서드를 호출 가능
- -> B 내에서 A메서드를 호출하게 한다. 당연함
- Introduce Parameter Object
- 전달되는 파라미터가 붙어 다닐 경우 객체로 묶는 것을 고려 ( Preserve Whole Object 와 비슷 )
- Remove Setting Method
- 필드 값이 변경 후 수정되면 안될 경우 쓰기 메서드를 제거
- Hide Method
- 메서드가 다른 클래스에 사용되지 않을 경우 private으로 작성
- Replace Constructor with Factory Method
- 생성자 내에 생성할 객체 타입이 변하거나 로직이 복잡할 경우 팩토리 메서드로 전환
- Replace Error Code with Exception
- 에러 코드 대신 예외를 사용하라는 내용
- ruby에 예외 기능이 있어 사용하라는 것 같은데 언어에 따라 다를 수도 있을 것 같다.
- 시스템 상 예측하지 못한 에러면 예외를 발생시키는게 맞지만 단지 비즈니스 로직 상 실패를 처리할 경우를 모두 예외처리를 하는 것도 안좋은 방식이라는 글을 본적이 있다. ( Java를 비판하는 글로 기억 )
- Introduce Gateway / Introduce Expression Builder
- 외부 시스템이나 리소스의 복합 API를 연동해야 할 경우 공통으로 지나는 부분을 모듈화
-
- builder 패턴
- 현재 앱단 코드의 service 쪽을 생각하면 된다.
Chap 11: 일반화 처리
- Pull Up Method <-> Push Down Method
- 여러 하위클래스에 중복 메서드가 있으면 상위 클래스로 메서드 이동
- 상위클래스에 있는 기능이 일부 하위클래스에만 관련돼 있을 때는 관련 하위클래스로 이동
- Extract Module <-> Inline Module
- 여러 클래스에 같은 동작이 있을 때는 모듈로 분리하고 각 클래스에서 include
- 문법은 다르지만 java의 interface와 비슷한 의도로 추정
- Extract Subclass
- 클래스에 일부 인스턴스만 사용하는 기능이 있을 경우
- 하위클래스 작성 후 Push Down Method
- Introduce Inheritance
- 여러 클래스의 기능이 비슷할 경우
- 상위 클래스 작성 후 공통 기능을 Pull up Method
- Collapse Hierarchy
- 상위클래스와 하위클래스가 별로 다르지 않을때는 합침
- Form Template Method
- 하위클래스들의 메서드의 진행 순서는 비슷하지만 같지는 않은 경우 (a
+b
), (a+b
) - 메서드 내 공통 부분을 묶어서 템플릿 메서드의 조합으로 만듬 (a와 b)
- 그 후 Pull up Method (a+b)
- 하위클래스들의 메서드의 진행 순서는 비슷하지만 같지는 않은 경우 (a
- Replace Inheritance with Delegation
- 하위클래스가 상위클래스의 극히 일부만 사용할 경우 상속의 의미가 적어진다.
- 필드를 추가 후 필요한 기능만 위임해서 사용
- ex) Hash의 기능 일부를 쓴다고 Hash를 통째로 상속 받는 것보단 Hash 형태 의 필드를 추가 후 필요한 기능만 위임 처리
- Replace Delegation with Hierarchy
- 대리 객체의 위임을 많이 작성하게 된다면 차라리 계층 구조로 만드는 것이 나음
- Replace Abstract Superclass with Module
- 상속 구조이나 상위클래스를 인스턴스화 시킬 의도가 없을 때
- Java의 Abstract가 ruby에는 없으므로 module로 구현
비판 - 마틴 파울러 - 내용이 길고 장황한 것에 비해 남는게 없다 ? - -> 그나마 호평을 받은 책은 '클린 코드' 정도.