ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Refactoring] 3장 : 코드에서 나는 악취
    책/Refactoring 2판 2024. 2. 14. 11:22
    반응형

    기이한 이름

    • 함수, 변수, 클래스 등은 이름만 보고도 무슨 일을 할지 알 정도로 잘 써야 한다.
    • 함수 선언 바꾸기, 변수 이름 바꾸기, 필드 이름 바꾸기

    중복 코드

    • 코드가 여러 군데에 똑같은게 있다면 하나로 통합하자
    • 두 메서드가 똑같은 표현식 사용 → 함수 추출하기 를 통해 양쪽 모두 추출한 메서드를 호출하도록
    • 비슷한 표현식 → 문장 슬라이드하기 를 통해 비슷한 부분을 한 곳에 모아 함수 추출
    • 서브 클래스들에 코드 중복 → 메서드 올리기 를 적용하여 부모로 옮김

    긴 함수

    • 함수는 잘게 쪼개는 것이 좋다 ← 이때 주의할 점은 함수의 이름이 반드시 명확해야 함
      • 함수 내부의 코드의 목적을 분명하게 나타내줘야함
      • 그래야만 함수를 잘게 쪼개는 의미가 있다.
    • 함수 추출하기
    • 임시 변수 줄이기 - 임시 변수를 질의 함수로 바꾸기
    • 매개 변수 줄이기 - 매개변수 객체 만들기, 객체 통째로 넘기기
      • 위를 다 적용해도 임시 변수와 매개변수가 많다면 함수를 명령으로 바꾸기
    • 조건문 - 조건문 분해하기
    • switch 문 → case 마다 함수 추출하기 - 각 case의 본문을 함수 호출문 하나로 바꿈
    • 같은 조건을 기준으로 나뉘는 switch 문이 여러개라면 조건문을 다형성으로 바꾸기
    • 반복문 - 반복문 쪼개기

    긴 매개변수 목록

    • 다른 매개변수에서 값을 얻을 수 있는 매개변수 ← 매개변수를 질의 함수로 바꾸기
    • 사용 중인 데이터 구조에서 값 뽑아 각각을 매개변수로 제공한다면 → 객체 통째로 넘기기
    • 항상 함께 전달되는 매개변수 하나로 묶기 → 매개변수 객체 만들기
    • 플래그 역햘의 매개변수 제거하기 → 플래그 인수 제거하기
    • 여러 함수가 특정 매개변수들의 값을 공통으로 사용 → 여러 함수를 클래스로 묶기

    전역 데이터

    • 그냥 쓰지 말자
    • 전역 데이터를 감싸기 → 변수 캡슐화하기
    • 클래스 변수, 싱글톤에서도 전역 변수와 비슷한 문제 발생 가능

    가변 데이터

    • 데이터가 예상치 못하게 바뀌는 경우가 많음
    • 함수형 프로그래밍에서는 데이터가 절대 변하지 않고, 데이터를 변경하려면 반드시 원래 데이터를 그대로 둔 채 변경된 복사본을 리턴하게 된다.
    • 변수 캡슐화하기 → 정해놓은 함수를 거쳐야만 값을 수정하도록
    • 하나의 변수에 용도가 다른 값들을 저장하느라 값 갱신하는 경우 → 변수 쪼개기
    • 갱신 로직을 다른 코드와 떨어뜨려놓기 → 문장 슬라이드하기, 함수 추출하기
    • API 만들때 부작용이 있는 코드를 호출할 수 없게 → 질의 함수와 변경 함수 분리하기
    • 세터 가급적 제거하기 → 세터 제거하기

    뒤엉킨 변경

    • 단일 책임 원칙 (SRP)이 제대로 지켜지지 않을 떄 나타남
    • 한 코드에 섞여 들어감
    • 무언가 추가될 때마다 함수 몇개가 동시에 바뀌어야 하는 경우
    • 순차적으로 실행되는 게 자연스러운 맥락이면 다음 맥락에 필요한 데이터를 특정 데이터 구조에 담아 전달 → 단계 쪼개기
    • 전체 처리 과정 곳곳에 각기 다른 맥락의 함수를 호출하는 빈도가 높다 → 각 맥락에 해당하는 모듈을 만들어서 관련 함수를 모음 (함수 옮기기)
      • 이 과정에서 여러 맥락의 일에 관여하는 함수가 있다면 함수 추출하기
    • 모듈이 클래스라면 클래스 추출하기

    → 맥락별로 분리

    산탄총 수술

    • 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많아질 때
    • 여러 코드에 흩뿌려짐
    • 함께 변경되는 대상들을 한 모듈에 묶음 → 함수 옮기기, 필드 옮기기
    • 비슷한 데이터를 다루는 함수가 많다면 → 여러 함수를 클래스로 묶기
    • 데이터 구조를 변환하거나 보강하는 함수 → 여러 함수를 변환 함수로 묶기
      • 이렇게 묶은 함수들의 출력 결과를 묶어서 다음 단계 로직으로 전달할 수 있다 → 단계 쪼개기
    • 어설프게 분리된 로직 → 함수 인라인하기, 클래스 인라인하기

    → 맥락별로 모음

    기능 편애

    • 어떤 함수가 자신이 속한 모듈의 함수나 데이터보다 다른 모듈의 함수나 데이터와 더 상호작용을 많이 하는 경우
    • 이 함수를 데이터 근처로 옮겨줌 → 함수 옮기기
    • 함수의 일부에서만 기능을 편애 → 함수 추출하기 로 독립 함수로 뺀 다음 원하는 모듈로 보냄 (함수 옮기기)
    • 함수 추출하기 로 함수를 여러 조각으로 나누고 각각을 적합한 모듈로 옮긴다.

    데이터 뭉치

    • 몰려다니는 데이터 뭉치를 위한 클래스를 만들어줘야 한다.
    • 필드 형태의 데이터 뭉치 → 클래스 추출하기 로 하나의 객체로 묶음
    • 메소드 시그니처 → 매개변수 객체 만들기, 객체 통째로 넘기기

    기본형 집착

    • 전화번호와 같이 단순한 문자열로는 표현이 아쉬운 경우, 이를 객체로 만들어 표현한다.
    • 기본형을 객체로 바꾸기
    • 기본형으로 표현된 코드가 조건부 동작 제어하는 타입 코드라면 → 타입 코드를 서브클래스로 바꾸기, 조건부 로직을 다형성으로 바꾸기
    • 자주 몰려다니는 기본형 그룹 → 클래스 추출하기, 매개변수 객체 만들기

    반복되는 switch문

    • 똑같은 조건부 로직이 여러 곳에서 반복되는 경우
    • switch나 if를 무조건 없애는 게 아니고, 다른 코드에서 반복되는 로직이 있을 경우 → 조건부 로직을 다형성으로 바꾸기

    반복문

    • 반복문은 파이프라인을 사용하자 → 반복문을 파이프라인으로 바꾸기

    성의 없는 요소

    • 본문 코드를 그대로 쓰는 것과 다름없는 함수, 메서드가 하나뿐인 클래스
    • 역할을 제대로 하지 못하는 클래스, 함수 등은 인라인으로 보내주는 것 → 함수 인라인하기, 클래스 인라인하기
    • 상속 계층의 경우 계층을 합쳐버린다. → 계층 합치기

    추측성 일반화

    • 미래에 사용될 것으로 예측해 쓸데없이 적어놓은 코드
    • 하는 일이 없는 추상 클래스 → 계층 합치기
    • 쓸데없이 위임하는 코드 → 함수 인라인하기, 클래스 인라인하기
    • 본문에서 사용되지 않는 매개변수 → 함수 선언 바꾸기
    • 테스트 코드 말고는 사용하지 않는 함수, 클래스 → 테스트 케이스 제거한 뒤 죽은 코드 제거하기

    임시 필드

    • 특정 상황에서만 값이 채워지는 필드
    • 보통 필드는 다 채워질 것이라고 기대하므로 추출해내야함
    • 클래스 추출하기 → 함수 옮기기 : 임시 필드를 사용하는 함수들을 모조리 새 클래스에 넣는다.
    • 임시 필드의 유효성을 검사하고 동작하는 조건부 로직 → 특이 케이스 추가하기 로 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어 제거

    메시지 체인

    • this.property.name.first 등 .으로 쭉 이어지는 경우
    • 중간 단계를 숨겨줌으로써 해결 가능 → 위임 숨기기
    • 이러면 중간 객체들이 모두 중개자가 되어버림
      • 최종 결과 객체가 어떻게 쓰이는지 확인
    • 함수 추출하기 로 결과 객체를 사용하는 코드 일부를 빼내어 함수 옮기기 로 체인을 숨길 수 있는지 확인

    중개자

    • 하는 일 없이 중개만 하는 코드들을 제거해준다.
    • 중개자 제거하기 → 직접 소통하도록 만들어줌
    • 위임 메서드를 제거하고 남는 일이 거의 없다 → 호출하는 쪽으로 함수 인라인하기

    내부자 거래

    • 모듈 사이에 데이터 거래가 많은 경우 이를 최소로 줄여야함
    • 함수 옮기기, 필드 옮기기 로 내부 거래 떼어놓음
    • 여러 모듈이 같은 관심사 공유 → 제 3의 모듈을 만들거나 위임 숨기기
    • 상속 → 서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기

    거대한 클래스

    • 너무 큰 클래스는 필드 중복이 생기기 쉽다.
    • 필드들 일부를 따로 묶음 → 클래스 추출하기
      • 이렇게 분리한 컴포넌트들이 상속 관계여야 한다면 → 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기
    • 클래스 안에서 유용한 기능 그룹을 찾았다면 → 클래스 추출하기, 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기

    서로 다른 인터페이스의 대안 클래스들

    • 클래스의 장점 : 언제든 다른 클래스로 바꿀 수 있다. 단, 인터페이스가 같아야 함
    • 메서드 시그니처 일치시켜야함 → 함수 선언 바꾸기
    • 부족하다면 인터페이스가 같아질 때까지 필요 동작들을 클래스 안으로 밀어넣음 → 함수 옮기기
    • 대안 클래스 사이에 중복 코드가 생기면 슈퍼클래스 추출하기 고려

    데이터 클래스

    • 게터/세터 및 필드로만 이루어진 클래스
    • 변하지 않아야 하는 필드에서는 세터를 없앤다. → 세터 제거하기
    • public 필드 → 레코드 캡슐화하기
    • 다른 클래스의 메서드에서 데이터 클래스의 게터, 세터 자주 사용 → 함수 옮기기로 그 메서드를 데이터 클래스로 옮길 수 있는지
    • 통째로 옮기기 어렵다면 일부를 함수 추출하기 한 뒤 그 부분만 별도 메서드로 뽑아냄

    상속 포기

    • 물려받고 싶지 않는 부모 필드, 메서드는 → 메서드 내리기, 필드 내리기 로 모두 서브클래스로 옮겨줌
    • 서브클래스가 부모의 동작은 필요로 하지만 인터페이스는 따르고 싶지 않을 경우 → 서브클래스를 위임으로 바꾸기, 슈퍼클래스를 위임으로 바꾸기

    주석

    • 주석을 남기는 것은 좋으나, 과도해선 안된다
    • 주석이 필요없도록 함수로 추출해내는 것이 중요 → 함수 추출하기
    • 여전히 설명이 필요하다 → 함수 선언 바꾸기
    • 시스템 동작 선행조건 명시하고 싶다 → 어서션 추가하기

     

    https://product.kyobobook.co.kr/detail/S000001810241

    반응형
Designed by Tistory.