https://www.inflearn.com/course/리팩토링
기본형 집착
냄새 11. 기본형 집착
- 애플리케이션이 다루고 있는 도메인에 필요한 기본 타입을 만들지 않고 프로그래밍 언어가 제공하는 기본 타입을 사용하는 경우가 많다.
- 예) 전화번호, 좌표, 돈, 범위, 수량 등 - 기본형으로는 단위 (인치 vs 미터) 또는 표기법을 표현하기 어렵다.
- 관련 리팩토링 기술
- 기본형을 객체로 바꾸기
- 타입 코드를 서브클래스로 바꾸기
- 조건부 로직을 다형성으로 바꾸기
- 클래스 추출하기
- 매개변수 객체 만들기
리팩토링 30. 기본형을 객체로 바꾸기
- 개발 초기에는 기본형 (숫자 또는 문자열)으로 표현한 데이터가 나중에는 해당 데이터와 관련 있는 다양한 기능을 필요로 하는 경우가 발생한다.
- 예) 문자열로 표현하던 전화번호의 지역 코드가 필요하거나 다양한 포맷을 지원하는 경우.
- 예) 숫자로 표현하던 온도의 단위 (화씨, 섭씨)를 변환하는 경우. - 기본형을 사용한 데이터를 감싸 줄 클래스를 만들면, 필요한 기능을 추가할 수 있다.
리팩토링 31. 타입 코드를 서브클래스로 바꾸기
- 비슷하지만 다른 것들을 표현해야 하는 경우, 문자열, 열거형, 숫자 등으로 표현하기도 한다.
- 예) 주문 타입, "일반 주문", "빠른 주문"
- 예) 직원 타입, "엔지니어", "매니저", "세일즈" - 타입을 서브클래스로 바꾸는 계기
- 조건문을 다형성으로 표현할 수 있을 때, 서브클래스를 만들고 "조건부 로직을 다형성으로 바꾸기" 를 적용한다.
- 특정 타입에만 유효한 필드가 있을 때, 서브클래스를 만들고 "필드 내리기"를 사용한다.
리팩토링 32. 조건부 로직을 다형성으로 바꾸기
- 복잡한 조건식을 상속과 다형성을 사용해 코드를 보다 명확하게 분리할 수 있다.
- switch 문을 사용해서 타입에 따라 각기 다른 로직을 사용하는 코드
- 기본 동작과 (타입에 따른) 특수한 기능이 섞여있는 경우에 상속 구조를 만들어서 기본 동작을 상위클래스에 두고 특수한 기능을 하위클래스로 옮겨서 각 타입에 따른 "차이점"을 강조할 수 있다.
- 모든 조건문을 다형성으로 옮겨야 하는가? 단순한 조건문은 그대로 두어도 좋다.
오직 복잡한 조건문을 다형성을 활용해 좀 더 나은 코드로 만들 수 있는 경우에만 적용한다. (과용을 조심하자.)
반복되는 switch 문
냄새 12. 반복되는 switch 문
- 예전에는 switch 문이 한번만 등장해도 코드 냄새로 생각하고 다형성 작용을 권장했다.
- 하지만 최근에는 다형성이 꽤 널리 사용되고 있으며, 여러 프로그래밍 언어에서 보다 세련된 형태의 switch 문을 지원하고 있다.
- 따라서 오늘날은 "반복해서 등장하는 동일한 switch 문"을 냄새로 여기고 있다.
- 반복해서 동일한 switch 문이 존재할 경우, 새로운 조건을 추가하거나 기존의 조건을 변경할 때 모든 switch문을 찾아서 코드를 고쳐야 할지도 모른다.
반복문
냄새 13. 반복문
- 프로그래밍 언어 초기부터 있었던 반복문은 처음엔 별다른 대안이 없어서 간과했지만 최근 Java와 같은 언어에서 함수형 프로그래밍을 지원하면서 반복문에 비해 더 나은 대안책이 생겼다.
- "반복문을 파이프라인으로 바꾸는" 리팩토링을 적용하면 필터나 맵핑과 같은 파이프라인 기능을 사용해 보다 빠르게 어떤 작업을 하는지 파악할 수 있다.
리팩토링 33. 반복문을 파이프라인으로 바꾸기
- 콜렉션 파이프라인
- 고전적인 반복문을 파이프라인 오퍼레이션을 사용해 표현하면 코드를 더 명확하게 만들 수 있다.
- 필터 : 전달받은 조건의 true에 해당하는 데이터만 다음 오퍼레이션으로 전달.
- 맵 : 전달받은 함수를 사용해 입력값을 원하는 출력값으로 변환하여 다음 오퍼레이션으로 전달
성의없는 요소
냄새 14. 성의없는 요소
- 여러 프로그래밍적인 요소(변수, 메소드, 클래스 등)를 만드는 이유
- 나중에 발생할 변화를 대비해서...
- 해당 함수 또는 클래스를 재사용하려고...
- 의미있는 이름을 지어주려고... - 가끔 그렇게 예상하고 만들어 놓은 요소들이 기대에 부응하지 못하는 경우가 있는데 그런 경우에 해당 요소들을 제거해야 한다.
- 관련 리팩토링 기술
- "함수 인라인"
- "클래스 인라인"
- 불필요한 상속 구조는 "계층 합치기"를 사용할 수 있다.
리팩토링 34. 계층 합치기
- 상속 구조를 리팩토링 하는중에 기능을 올리고 내리다보면 하위클래스와 상위클래스 코드에 차이가 없는 경우가 발생할 수 있다. 그런 경우에 그 둘을 합칠 수 있다.
- 하위 클래스와 상위클래스 중에 어떤 것을 없애야 하는가? (둘 중에 보다 이름이 더 적절한 쪽을 선택하지만, 애매하다면 어느쪽을 선택해도 문제없다.)
- 인텔리제이에 push member down , pull member up 등으로 필드, 메서드를 옮길 수 있다.
추측성 일반화
냄새 15. 추측성 일반화
- 나중에 이러 저러한 기능이 생길 것으로 예상하여, 여러 경우에 필요로 할만한 기능을 만들어 놨지만 "그런 일은 없었고..." 결국에 쓰이지 않는 코드가 발생한 경우.
- XP의 YAGNI (You aren't gonna need it) 원칙을 따르자.
- 관련 리팩토링
- 추상 클래스를 만들었지만 크게 유요하지 않다면 "계층 합치기"
- 불필요한 위임은 "함수 인라인" 또는 "클래스 인라인"
- 사용하지 않는 매개변수를 가진 함수는 "함수 선언 변경하기"
- 오로지 테스트 코드에서만 사용하고 있는 코드는 "죽은 코드 제거하기"
리팩토링 35. 죽은 코드 제거하기
- 사용하지 않는 코드가 애플리케이션 성능이나 기능에 영향을 끼치지는 않는다.
- 하지만, 해당 소프트웨어가 어떻게 동작하는지 이해하려는 사람들에게는 꽤 고통을 줄 수 있다.
- 실제로 나중에 필요해질 코드라 하더라도 지금 쓰이지 않는 코드라면 (주석으로 감싸는게 아니라) 삭제해야 한다.
- 나중에 정말로 다시 필요해진다면 git과 같은 버전 관리 시스템을 사용해 복원할 수 있다.
임시 필드
냄새 16. 임시 필드
- 클래스에 있는 어떤 필드가 특정한 경우에만 값을 갖는 경우.
- 어떤 객체의 필드가 "특정한 경우에만" 값을 가진다는 것을 이해하는 것은 일반적으로 예상하지 못하기 때문에 이해하기 어렵다.
- 관련 리팩토링
- 클래스 추출하기를 사용해 해당 변수들을 옮길 수 있다.
- 함수 옮기기을 사용해서 해당 변수를 사용하는 함수를 특정 클래스로 옮길 수 있다.
- 특이 케이스 추가하기 를 적용해 특정한 경우에 해당하는 클래스를 만들어 해당 조건을 제거할 수 있다.
리팩토링 36. 특이 케이스 추가하기
- 어떤 필드의 특정한 값에 따라 동일하게 동작하는 코드가 반복적으로 나타난다면, 해당 필드를 감싸는 "특별한 케이스"를 만들어 해당 조건을 표현할 수 있다.
- 이러한 매커니즘을 "특이 케이스 패턴" 이라고 부르고 "Null Object 패턴"을 이러한 패턴의 특수한 형태라고 볼 수 있다.
메시지 체인
냄새 17. 메시지 체인
- 레퍼런스를 따라 계속해서 메소드 호출이 이어지는 코드.
- 예) this.member.getCredit().getLevel().getDescription() - 해당 코드의 클라이언트가 코드 체인을 모두 이해해야 한다.
- 체인 중 일부가 변경된다면 클라이언트의 코드도 변경해야 한다.
- 관련 리팩토링
- 위임 숨기기 를 사용해 메시지 체인을 캡슐화를 할 수 있다.
- 함수 추출하기 로 메시지 체인 일부를 함수로 추출한 뒤 함수 옮기기 으로 해당 함수를 적절한 이동을 할 수 있다.
리팩토링 37. 위임 숨기기
- 캡슐화란 어떤 모듈이 시스템의 다른 모듈을 최소한으로 알아야한다는 것이다. 그래야 어떤 모듈을 변경할 때, 최소한의 모듈만 그 변경에 영향을 받을 것이고, 그래야 무언가를 변경하기 쉽다.
- 처음 객체 지향에서 캡슐화를 배울 때 필드를 메소드로 숨기는 것이라 배우지만, 메소드 호출도 숨길 수 있다.
중재자
냄새 18. 중재자
- 캡슐화를 통해 내부의 구체적인 정보를 최대한 감출 수 있다.
- 그러나, 어떤 클래스의 메소드가 대부분 다른 클래스로 메소드 호출을 위임하고 있다면 중재자를 제거하고 클라이언트가 해당 클래스를 직접 사용하도록 코드를 개선할 수 있다.
- 관련 리팩토링
- 중재자 제거하기 리팩토링을 사용해 클라이언트가 필요한 클래스를 직접 사용하도록 개선할 수 있다.
- 함수 인라인 을 사용해서 메소드 호출한 쪽으로 코드를 보내서 중재자를 없앨 수도 있다.
- 슈퍼클래스를 위임으로 바꾸기
- 서브클래스를 위임으로 바꾸기
리팩토링 38. 중재자 제거하기
- 위임 숨기기의 반대에 해당하는 리팩토링
- 필요한 캡슐화의 정도는 시간에 따라 그리고 상황에 따라 바뀔 수 있다.
- 캡슐화의 정도를 "중재자 제거하기" 와 "위임 숨기기" 리팩토링을 통해 조절할 수 있다.
- 위임하고 있는 객체를 클라이언트가 사용할 수 있도록 getter를 제공하고, 클라이언트는 메시지 체인을 사용하도록 코드를 고친 뒤에 캡슐화에 사용했던 메소드를 제거한다.
- Law of Demter를 지나치게 따르기 보다는 상황에 맞게 활용하도록 하자.
- 디미터의 법칙, "가장 가까운 객체만 사용한다." - 중재자 제거 vs 중재자 제거 안하기 : 어느 한쪽이 옳은것은 없다.
리팩토링 39. 슈퍼클래스를 위임으로 바꾸기
- 객체지향에서 "상속"은 기존의 기능을 재사용하는 쉬우면서 강력한 방법이지만 때로는 적절하지 않은 경우도 있다.
- 서브클래스는 슈퍼클래스의 모든 기능을 지원해야 한다.
- Stack이라는 자료구조를 만들 때 List를 상속 받는것이 좋을까? - 서브클래스는 슈퍼클래스 자리를 대체하더라도 잘 동작해야 한다.
- 리스코프 치환 원칙 - 서브클래스는 슈퍼클래스의 변경에 취약하다.
- 그렇다면 상속을 사용하지 않는 것이 좋은가?
- 상속은 적절한 경우에 사용한다면 매우 쉽고 효율적인 방법이다.
- 따라서, 우선 상속을 적용한 이후에, 적절치 않다고 판단이 된다면 그때에 이 리팩토링을 적용하자. - command + p 를 통해 메소드의 변수명을 확인할 수 있다.
리팩토링 40. 서브클래스를 위임으로 바꾸기
- 어떤 객체의 행동이 카테고리에 따라 바뀐다면, 보통 상속을 사용해서 일반적인 로직은 슈퍼클래스에 두고 특이한 케이스에 해당하는 로직을 서브클래스를 사용해 표현한다.
- 하지만, 대부분의 프로그래밍 언어에서 상속은 오직 한번만 사용할 수 있다.
- 만약에 어떤 객체를 두가지 이상의 카테고리로 구분해야 한다면?
- 위임을 사용하면 얼마든지 여러가지 이유로 여러 다른 객체로 위임을 할 수 있다. - 슈퍼클래스가 바뀌면 모든 서브클래스에 영향을 줄 수 있다. 따라서 슈퍼클래스를 변경할 때 서브클래스까지 신경 써야 한다.
- 만약에 서브클래스가 전혀 다른 모듈에 있다면?
- 위임을 사용한다면 중간에 인터페이스를 만들어 의존성을 줄일 수 있다. - "상속 대신 위임을 선호하라."는 결코 "상속은 나쁘다." 라는 말이 아니다.
- 처음엔 상속을 적용하고 언제든지 이런 리팩토링을 사용해 위임으로 전환할 수 있다. - Loose Coupling : 느슨한 결합
- 생성자와 팩토리 메소드 차이
- 팩토리 메소드는 생성자와 달리 메소드 명칭을 좀 더 자유롭게 표현할 수 있음
내부자 거래
냄새 19. 내부자 거래
- 어떤 모듈이 다른 모듈의 내부 정보를 지나치게 많이 알고 있는 코드 냄새. 그로인해 지나치게 강한 결합도가 생길 수 있다.
- 적절한 모듈로 함수 옮기기 와 필드 옮기기 를 사용해서 결합도를 낮출 수 있다.
- 여러 모듈이 자주 사용하는 공통적인 기능은 새로운 모듈을 만들어 잘 관리하거나, 위임 숨기기 를 사용해 특정 모듈의 중재자처럼 사용할 수도 있다.
- 상속으로 인한 결합도를 줄일 때는 슈퍼클래스 또는 서브클래스를 위임으로 교체하기 를 사용할 수 있다.
냄새 20. 거대한 클래스
- 어떤 클래스가 너무 많은 일을 하다보면 필드도 많아지고 중복 코드도 보이기 시작한다.
- 클라이언트가 해당 클래스가 제공하는 기능 중에 일부만 사용한다면 각각의 세부 기능을 별도의 클래스로 분리할 수 있다.
- 클래스 추출하기 를 사용해 관련있는 필드를 한 곳으로 모을 수 있다.
- 상속 구조를 만들 수 있다면 슈퍼클래스 추출하기 또는 타입 코드를 서브클래스로 교체하기를 적용할 수 있다. - 클래스 내부에 산재하는 중복 코드는 메소드를 추출하여 제거할 수 있다.
리팩토링 41. 슈퍼클래스 추출하기
- 두개의 클래스에서 비슷한 것들이 보인다면 상속을 적용하고, 슈퍼클래스로 "필드 올리기" 와 "메소드 올리기"를 사용한다.
- 대안으로 "클래스 추출하기"를 적용해 위임을 사용할 수 있다.
- 우선은 간단히 상속을 적용한 이후, 나중에 필요하다면 "슈퍼클래스를 위임으로 교체하기"를 적용한다.
서로 다른 인터페이스의 대안 클래스들
냄새 21. 서로 다른 인터페이스의 대안 클래스들
- 비슷한 일을 여러 곳에서 서로 다른 규약을 사용해 지원하고 있는 코드 냄새.
- 대안 클래스로 사용하려면 동일한 인터페이스를 구현하고 있어야 한다.
- 함수 선언 변경하기 와 함수 옮기기 을 사용해서 서로 동일한 인터페이스를 구현하게끔 코드를 수정할 수 있다.
- 두 클래스에서 일부 코드가 중복되는 경우에는 "슈퍼클래스 추출하기"를 사용해 중복된 코드를 슈퍼클래스로 옮기고 두 클래스를 새로운 슈퍼클래스의 서브클래스로 만들 수 있다.
'리팩토링' 카테고리의 다른 글
[리팩토링] 코딩으로 학습하는 리팩토링 - 4 (0) | 2022.03.09 |
---|---|
[리팩토링] 코딩으로 학습하는 리팩토링 - 2 (0) | 2022.03.06 |
[리팩토링] 코딩으로 학습하는 리팩토링 - 1 (0) | 2022.02.28 |