리팩토링 2판 - 01
2020. 4. 30. 01:44ㆍ[공부] 독서/리팩토링 2판
기능 이동
좋은 소프트웨어 설계의 핵심은 모듈화 얼마나 잘 되어 있는지다. 객체 지향 프로그래밍의 핵심 모듈화 컨텍스트는 클래스다. 모듈성이란 프로그램 어딘가를 수정하려 할 때 해당 기능의 작은 일부만 이해해도 가능하게 해주는 능력이다.
프로그램 상당 부분이 동작을 구현하는 코드로 이뤄지지만 프로그램의 진짜 힘은 데이터 구조에서 나온다. 관련된 코드들은 가까이 모여있으면 좋다. 관련된 코드가 모여있다면 함수 추출이 쉬워진다. 필드와 함수가 적절한 위치에 있는 것이 좋다.
하지만 추상화 라는 것은 일반적으로 그 경계를 긋기가 힘들다. 그래서 코드 베이스의 기능범위가 달라지면 추상화의 경계가 움직인다. 함수 관점에서 생각해보면 초기에는 응집도가 높고 한가지 일만 수행하던 함수가 어느새 둘 이상의 다른 일을 수행하게 바뀔 수도 있다.
- 함수 옮기기
- 필드 옮기기
- 코드를 함수로 옮기기
- 코드를 호출한 곳으로 옮기기
- 인라인 코드를 함수 호출로 바꾸기
이름을 잘 지었다면 인라인 코드 대신 함수 이름을 넣어도 말이 된다. - 코드 슬라이드하기
- 반복문 쪼개기
반복문 안에 서로 다른 일들이 이뤄지고 있다면 반복문을 분리하고 함수를 쪼개야한다는 신호이다. - 반복문을 파이프라인으로 바꾸기
- 죽은 코드 제거하기
코드가 사용되지 않는다면 지워야한다. 다시 필요해질 날을 걱정하지 않아도 된다. 버전 관리 시스템이 알아서 기억해준다. 만약 코드가 훗날 사용될 것을 염려한다면 커밋 메세지라도 남겨놓아라.
명령-질의 분리 원칙
부수효과가 없는 코드들 끼리는 마음대로 배치할 수 있다. 현명한 프로그래머들은 부수효과가 없는 코드로 프로그래밍하려 한다. 부수효과를 없애기 위한 좋은 원칙 중 하나는 명령 질의 분리 원칙을 지키는 것이다.
명령 함수 : 동작의 수행
질의 함수 : 연산을 통해 답을 구하는 행위. 질의는 내부 또는 외부의 값을 변경하여 부수효과를 만들어선 안된다.
데이터 조직화
- 변수 쪼개기
변수에는 단 하나의 값만 대입해야한다. 대입이 두 번 이상 이뤄진다면 여러가지 역할을 수행하고 있다는 신호다. 역할이 둘 이상인 변수가 있다면 쪼개야한다. 예외는 없다. 역할 하나당 변수 하나다. - 필드 이름 바꾸기
- 파생 변수를 질의 함수로 바꾸기
파생 변수란 어떤 메소드의 결과를 담기 위한 멤버 변수를 말한다. 예를 들면 누적 값을 멤버 변수로 기록해둘 수 있을 것이다. 이때 누적값이 파생 변수다. 이를 질의 함수로 바꿔라. 누적값을 얻길 원한다면 질의 함수를 만들고 질의 함수가 누적 값을 계산해서 내려주도록 바꿔라. (함수형 프로그래밍) - 참조를 값으로 바꾸기
- 값을 참조로 바꾸기
논리적으로 같은 데이터를 물리적으로 복제해 사용할 때 가장 크게 문제되는 상황이, 데이터가 변경될 때 모든 복제본을 찾아서 갱신해줘야 한다는 것이다. 하나라도 놓치면 일관성이 깨진다. 이런 상황일 경우 복제 데이터가 아니라 참조로 바꾸는 것이 좋다. - 매직 넘버 바꾸기
조건부 로직 간소화
- 조건부 분해하기
- 조건식 통합하기
비교하는 조건은 다르지만 결과가 수행하는 동작이 같은 경우가 있다. 이럴 땐 조건 검사도 하나로 합치는 것이 낫다. - 중첩 조건문을 보호 구문으로 바꾸기
- 조건부 로직을 다형성으로 바꾸기
다형성은 객체 지향 프로그램의 핵심이지만 남용하기 쉽다. 모든 조건부 로직을 다형성으로 대체할 필요는 없다. 참고로 자바스크립트에서는 타입 계층 없이도 다형성을 표현할 수 있다. - 특이 케이스 추가하기
Null 객체 패턴과 같이 특이 케이스를 위한 클래스, 객체를 만들어서 처리하라. Null 객체 패턴은 특이 케이스 처리하기의 일부다. - Assertion 추가하기
Assertion 은 어떤 상태임을 가정한채 실행되는지 다른 개발자에게 알려주는 훌륭한 소통 도구이다. - 제어 플래그를 break, continue 문으로 바꾸기
API 리팩토링
좋은 API 란 데이터를 갱신하는 함수와 조회하는 함수를 명확히 구분한다.
- 변경 함수 : 데이터를 변경하는 함수
- 조회 함수 : 데이터를 조회하는 함수
- 명령 함수 : 동작을 수행하는 함수
- 질의 함수 : 연산을 통해 값을 계산하여 반환하는 함수. 질의 함수는 내외, 외부의 값을 변경시켜서 부수효과를 만들어선 안된다.
- 질의 함수와 변경 함수 분리하기
- 함수 매개변수화 하기
- 매개변수에 플래그 인수 제거하기
플래그 인수가 있다는 것은 함수를 분리할 수 있다는 증거다. 플래그 인수가 둘 이상이면 함수 하나가 너무 많은 일을 처리하고 있다는 신호다. 같은 로직을 조합해내는 더 간단한 함수를 만들 방법을 고민해봐야한다. - 매개변수로 객체를 넘기기
- 매개변수를 질의 함수로 바꾸기
- 질의 함수를 매개변수로 바꾸기
매개변수를 질의 함수로 바꿀 경우 문제점이 있다. 함수 안에서 매개변수의 질의 함수를 사용하는 경우 매개변수의 내부 값이 어떠냐에 함수의 결과 값이 달라진다. 이 경우 매개 변수의 참조값은 그대로라서 input 은 그대로인데 결과값이 달라진다. 즉 참조 투명성이 깨진다. 이럴 경우 차라리 질의 함수를 외부에서 매개변수로 전달해주도록 바꿔라.
참조 투명성 : 함수에 똑같은 값을 건네 호출하면 항상 똑같이 동작한다는 특징, 이런 함수는 동작을 예측하고 테스트하기가 훨씬 쉽다. - 세터 제거하기
세터를 제거하면 생성자에서만 값이 설정되고 더 이상 수정하지 않겠다는 의도를 명백히 할 수 있다. - 생성자를 팩토리 패턴으로 바꾸기
- 함수를 명령으로 바꾸기
Command 패턴 - 명령을 함수로 바꾸기
- 수정된 값 반환하기
- 오류 코드를 예외로 처리하기
예외를 사용하면 오류를 일일이 검사하거나 오류를 식별해 콜스택 위로 던지는 일을 신경쓰지 않아도 된다. - 예외를 사전 확인하기
상속 다루기
메소드의 전체 흐름이 비슷하다면 템플릿 메소드 패턴을 사용하는 것도 좋다.
- 메소드 올리기
- 필드 올리기
- 생성자 올리기
- 메소드 내리기
- 필드 내리기
- 타입 코드를 서브클래스로 바꾸기
타입 코드를 서브클래스로 바꿀 때 간접 상속으로도 문제를 해결할 수 있다. - 서브클래스 제거하기
서브클래스의 활용 범위가 작다면 굳이 존재할 필요가 없다. - 슈퍼클래스 추출하기
- 계층 합치기
계층 구조가 진화하면서 부모와 자식 클래스가 너무 비슷해져서 독립적으로 존재 할 이유가 사라지기도 한다. 이럴 때는 그 둘을 하나로 합치는 것이 좋다. - 서브클래스를 위임으로 바꾸기
유명한 원칙 중 하나인 '클래스 상속보다는 객체 컴포지션을 사용하라.' 와 같은 내용이다. 컴포지션이란 사실상 위임과 같은 말이다.
상속에는 치명적인 단점이있다 가장 명확한 단점은 상속을 한번만 쓸 수 있다는 것이다. 무언가 달라져야 하는 이유가 여러개여도 상속에서는 단 하나의 이유만 선택해서 기준으로 삼을 수 밖에 없다. 위임은 객체 사이의 일반적인 관계이므로 상호 작용에 필요한 인터페이스를 명확히 정의 할 수 있다. 상속보다 결합도가 훨씬 약하다. 저자의 경우 처음에는 상속으로 접근하고 문제가 생기면 위임으로 갈아탄다. - 슈퍼클래스를 위임으로 바꾸기
상속을 잘못 적용한 예로 자바의 Stack 클래스가 유명하다. 자바의 Stack 은 List 를 상속 하고 있다. 데이터를 저장하고 조작하는 List 의 기능을 재활용 하겠다는 생각으로 만들어졌지만, 이 상속에는 문제가 있다. List 연산중 스택에서는 사용되지 않는 추상 메소드가 있다. 따라서 Stack 인터페이스에 불필요한 List 의 인터페이스가 그대로 노출된다. Stack 에서 List 객체를 필드에 저장해두고 필요한 기능만 위임했다면 더 좋았을 것이다.
'[공부] 독서 > 리팩토링 2판' 카테고리의 다른 글
리팩토링 2판 - 00 (0) | 2020.04.27 |
---|