[Refactoring] Refactoring

Refactoring-book

목차

구린내와 탈취 기법

구린내 탈취를 위한 리팩토링 기법
swich문 조건문을 재정의로 전환,분류 부호를 하위클래스로 전환, 분류 부호를 상태/전략 패턴으로 전환, 매개변수를 메서드로 전환, Null 검사를 널 객체에 위임
강박적 기본 타입 사용 데이터 값을 객체로 전환, 클래스 추출, 매개변수 세트를 객체로 전환, 배열을 객체로 전환, 분류 부호를 하위클래스로 전환, 분류 부호를 상태/전략 패턴으로 전환
과다한 매개변수 매개변수 세트를 메서드로 전환, 매개변수 세트를 객체로 전환, 객체를 통째로 전달
과잉 중개 메서드 과잉 중개 메서드 제거, 메서드 내용 직접 삽입, 위임을 상속으로 전환
기능의 산재 메서드 이동, 필드 이동, 클래스 내용 직접 삽입
데이터 뭉치 클래스 추출, 매개변수 세트를 객체로 전환, 객체를 통째로 전달
데이터 클래스 메서드 이동, 필드 캡슐화, 컬렉터 캡슐화
막연한 범용 코드 계층 병합, 클래스 내용 직접 삽입, 매개변수 제거, 메서드명 변경
메시지 체인 대리 객체 은폐
미흡한 라이브러리 클래스 외래 클래스에 메서드 추가, 국소적 상속확장 클래스 사용
방대한 클래스 클래스 추출, 하위클래스 추출, 인터페이스 추출, 데이터 값을 객체로 전환
방치된 상속물 상속을 위임으로 전환
불필요한 주석 메서드 추출, 어설션 넣기
수정의 산발 클래스 추출
인터페이스가 다른 대용 클래스 메서드명 변경, 메서드 이동
임시 필드 클래스 추출, Null 검사를 널 객체에 위임
잘못된 소속 메서드 이동, 필드 이동, 메서드 추출
장황한 메서드 메서드 추출, 임시변수를 메서드 호출로 전환, 메서드를 메서드 객체로 전환, 조건문 쪼개기
중복코드 메서드 추출, 클래스 추출, 메서드 상향, 템블릿 메서드 형성
지나친 관여 메서드 이동, 필드 이동, 클래스의 양방향 연결을 단방향으로 전환, 상속을 위임으로 전환, 대리 객체 은폐
직무유기 클래스 클래스 내용 직접 삽입, 계층 병합
평행 상속 계층 메서드 이동, 필드 이동

메서드 정리

메서드 추출

메서드 내용 직접 삽입

임시변수 내용 직접 삽입 / 임시변수를 메서드 호출로 전환

수정 전

  double basePrice = as.getBasePrice();
  return (basePrice > 1000);

수정 후

  return (as.getBasePrice() > 1000);

직관적 임시변수 사용

수정 전

  if(row.getColum(0) == 'A'
    || (row.getColum(0) == 'B'
        && row.getColum(1) == 'Y'
        && row.getColum(2) == 'Y'
        )
    || row.getColum(0) == 'C')
  {        
  }

수정 후

  boolean isLinkedApplication = row.getColum(0) == 'A'    //연계신청 했다
  boolean isNotLinkedApplication = row.getColum(0) == 'B' //연계신청 안했다
  boolean isSumApplication = row.getColum(0) == 'C'       //합산신청 했다
  boolean isMovingDateProper = row.getColum(1) == 'Y'     //이동일 적정
  boolean isAppropriateDueDate = row.getColum(2) == 'Y'   //신청기한 적절.
  if(isLinkedApplication || (isNotLinkedApplication && isMovingDateProper && isAppropriateDueDate) || isSumApplication) {        
  }

임시변수 분리

매개변수로의 값 대입 제거

메서드를 메서드 객체로 전환

알고리즘 전환

객체 간의 기능 이동

메서드 이동

필드 이동

클래스 추출

클래스 내용 직접 삽입

대리 객체 은폐

대리 객체 은폐

과잉 중개 메서드 제거

과잉 중개 메서드 제거

외래 클래스에 메서드 추가

수정 전

Date newStart = new Date (previousEnd.getYear() , previousEnd.getMonth() , previousEnd.getDate() + 1);

수정 후

Date newStart = nextDay(previousEnd);

private static Date nextDay(Date arg) {
  return new Date (arg.getYear() ,arg.getMonth() , arg.getDate() + 1);
}

국소적 상속확장 클래스 사용

데이터 체계화

필드 자체 캡슐화

수정 전

private int low , high;
boolean includes (int arg ) {
  return arg >= low && arg <= high;
}

수정 후

private int low , high;
boolean includes (int arg) {
  return arg >= getLow() && arg <= getHigh();
}
int getLow() {return low;}
int getHigh() {return high;}

데이터 값을 객체로 전환

데이터 값을 객체로 전환

수정 전

// 개발 초기 단계에서는 단순 정보를 간단한 데이터 항목으로 표현. Order 객체로 데이터 Get한다.
class Order {
  public Order (String customer) {
    customer = customer;
  }
  public String getCustomer() {
    return customer;
  }
  public void setCustomer (String arg) {
    customer = arg;
  }
  private String customer;
}

class OrderService ...
  private int numberOfOrdersFor(Collection orders, String customer) {
    int result = 0;
    Iterator iter = orders.iterator();
    while (iter.hasNext()) {
    Order each = (Order) iter.next();
    if (each.getCustomer().equals (customer)) result++;
    return result;
  }
...

수정 후

// 간단한 항목이 점점 복잡해진다(ex.형식화,지역번호 추출등). 항목객체(Customer)를 추가하여 항목들을 객체로 관리한다.

// Order 객체 수정.
class Order {
  public Order (String customer) {
    customer = new Customer(customer);
  }
  public String getCustomer() {
    return customer.getName();
  }
  private Customer customer;
  public void setCustomer (String arg) {
    customer = new Customer(arg );;
  }
}

// Customer 객체 추가
class Customer {
  public Customer (String name) {
    name = name;
  }
  public String getName() {
    return name;
  }
  private final String name;
}

class OrderService ...
  private int numberOfOrdersFor(Collection orders, String customer) {
    int result = 0;
    Iterator iter = orders.iterator();
    while (iter.hasNext()) {
    Order each = (Order) iter.next();
    if (each.getCustomer().equals (customer)) result++;
    return result;
  }
...

값을 참조로 전환

참조를 값으로 전환

배열을 객체로 전환

관측 데이터 복제

클래스의 단방향 연결을 양방향으로 전환

클래스의 양방향 연결을 단방향으로 전환

마법 숫자를 기호 상수로 전환

double potentialEnergy(double mass , double height) {
  return mass * 9.81 * height;
}

수정 후

static final double GRAVITATIONAL_CONSTANT = 9.81;

double potentialEnergy(double mass , double height) {
  return mass * GRAVITATIONAL_CONSTANT * height;
}

필드 캡슐화

수정 전

public String name;

수정 후

private String name;
public String getName() {return name;}
public void setName(String arg) {name = arg;}

컬렉션 캡슐화

수정 전

class Course...
  public Course (String name , boolean isAdvanced) {...};
  public boolean isAdvanced () {...};
...

class Person ...
  public Set getCourses () {
    return courses;
  }
  public void setCourses (Set arg ) {
    courses = arg;
  }
  private Set courses ;
...

// 컬렉션 사용 코드
Person kent = new Person();
Set s = new HashSet();
s.add (new Course("스몰토크 프로그래밍" false));
s.add(new Course ("싱글몰트 위스키 음미하기" true));
kent.setCourses(s);
Assert.equals (2, kent.getCourses().size());
Course refact = new Course ("리맥토링 true");
kent.getCourses().add(refact);
kent.getCourses().add(new Course ("지독한 빈 정 거림", false));
Assert.equals(4, kent.getCourses().size()) ;
kent.getCourses().remove(refact);
Assert.equals(3, kent.getCourses().size());

수정 후

class Person {
  private Set courses = new HashSet();

  public void addCourse(Course arg) (
    courses.add(arg);
  }
  public void removeCourse(Course arg) {
    courses.remove (arg );
  }
}

// 컬렉션 사용 코드
Person kent = new Person();
kent.addCourse(new Course ("스몰토크 프로그래밍’" fal se));
kent.addCourse(new Course ("싱글몰트 위스키 음미하기 true));
...

레코드를 데이터 클래스로 전환

분류 부호를 클래스로 전환

수정 전

public static final int 0 = 0;
public static final int A = 1;
public static final int B = 2;
public static final int AB = 3;
private int bloodGroup;

수정 후

public static final BloodGroup 0 = new BloodGroup(0);
public static final BloodGroup A = new BloodGroup(1);
public static final BloodGroup B = new BloodGroup(2);
public static final BloodGroup AB = new BloodGroup(3);
private static final BloodGroup[] values = {O, A, B, AB};

private final int code;
private BloodGroup (int code ) {
code = code;

분류 부호를 하위 클래스로 전환

분류 부호를 하위 클래스로 전환

분류 부호를 상태/전략 패턴으로 전환

분류 부호를 상태/전략 패턴으로 전환

하위클래스를 필드로 전환

하위클래스를 필드로 전환

수정 전

abstract class Person {
  abstract boolean isMale() ;
  abstract char getCode() ;
}
class Male extends Person {
  boolean isMale () {
    return true;
  }
  char getCode() {
    return "M";
  }
}

수정 후

class Person{
  private final boolean isMale ;
  private final char code;

  protected Person (boolean isMale , char code ) {
    isMale = isMale ;
    code = code;
  }

  static Person createMale () {
    return new Person (true, "M");
  }
}

조건문 간결화

조건문 쪼개기

수정 전

if (date.before (SUMMER_START) || date.after(SUMMER_END))
  charge = quantity * winterRate + winterServiceCharge;
else charge = quantity * summerRate;

수정 후

if (notSummer(date))
  charge = winterCharge(quantity);
else charge = summerCharge (quantity);

중복 조건식 통합

수정 전

double disabilityAmount() {
  if (seniority < 2) return 0;
  if (monthsDisabled > 12) return 0;
  if (isPartTime) return 0;
  // 장애인 공제액 산출
}

수정 후

double disabilityAmount () {
  if (isNotEligableForDisability()) return 0;
  // 장애인 공제액 산출
}
boolean isNotEligibleForDisability () {
  return ((seniority < 2) || (monthsDisabled > 12) || (isPartTime));
}

조건문의 공통 실행 코드 빼내기

수정 전

if (isSpecialDealO) {
  total = price * 0.95;
  send() ;
} else {
  total = price * 0.98;
  send () ;
}

수정 후

if (isSpecialDealO)
  total = price * 0.95;  
else
  total = price * 0.98;
send() ;

제어 플래그 제거

여러 겹의 조건문을 감시 절로 전환

수정 전

double getPayAmount() {
  double result ;
  if (isDead) result = deadAmount();
  else {
    if (isSeparated) result = separatedAmount();
    else {
      if (isRetired) result = retiredAmount();
      else resul t = normalPayAmount ();
    }
  }
  return result ;
}

수정 후

double getPayAmount() {
  if (isDead) return deadAmount();
  if (isSeparated) return separatedAmount();
  if (isRetired) return retiredAmount();
  return normalPay Amount();
}

조건문을 재정의로 전환

Null 검사를 널 객체에 위임

어설션 넣기

메서드 호출 단순화

메서드명 변경

매개변수 추가

매개변수 제거

상태 변경 메서드와 값 반환 메세드를 분리

상태 변경 메서드와 값 반환 메서드를 분리

메서드를 매개변수로 전환

메서드를 매개변수로 전환

매개변수를 메서드로 전환

객체를 통째로 전달

매개변수 세트를 메서드로 전환

매개변수 세트를 객체로 전환

쓰기 메서드 제거

메서드 은폐

생성자를 팩토리 메서드로 전환

하향 타입 변환을 캡슐화

에러 부호를 예외 통지로 전환

예외 처리를 테스트로 교체

일반화 처리

필드 상향

메서드 상향

생성자 내용 상향

메서드 하향

필드 하향

하위클래스 추출

하위클래스 추출

상위클래스 추출

상위클래스 추출

인터페이스 추출

계층 병합

템플릿 메서드 형성

템플릿 메서드 형성

수정전

public class Customer {
    public String statement() {
        return new TextStatement().value(this);
    }
    public String htmlStatement() {
        return new HtmlStatement().value(this);
    }
}
public class HtmlStatement {

    public String value(Customer customer) {
        int multiply = 10 * 8;
        String result = "HtmlStatement : 고객님 대여 기록";

        for (int i = 0; i < 6; i++) {
            result += "누적 포인트 HtmlStatement";
        }
        result += "누적 대여료 HtmlStatement";
        return result;
    }

}
public class TextStatement {

    public String value(Customer customer) {
        int multiply = 10 * 8;
        String result = "TextStatement : 고객님 대여 기록";

        for (int i = 0; i < 6; i++) {
            result += "누적 포인트 TextStatement";
        }
        result += "누적 대여료 TextStatement";
        return result;
    }

}

수정 후

public class Customer {
    public String statement() {
        return new TextStatement().value(this );
    }
    public String htmlStatement() {
        return new HtmlStatement().value(this );
    }
}
public abstract class Statement {
    public abstract String headerString();
    public abstract String eachString();
    public abstract String footerString();

    public String value(Customer customer) {
        int multiply = 10 * 8;
        String result = headerString();

        for (int i = 0; i < 6; i++) {
            result += eachString();
        }
        result += footerString();
        return result;
    }
}
public class HtmlStatement extends Statement{

    @Override
    public String headerString() {
        return "HtmlStatement : 고객님 대여 기록";
    }

    @Override
    public String eachString() {
        return "누적 포인트 HtmlStatement";
    }

    @Override
    public String footerString() {
        return "누적 대여료 HtmlStatement";
    }
}
public class TextStatement extends Statement{

    @Override
    public String headerString() {
        return "TextStatement : 고객님 대여 기록";
    }

    @Override
    public String eachString() {
        return "누적 포인트 TextStatement";
    }

    @Override
    public String footerString() {
        return "TextStatement : 고객님 대여 기록";
    }
}

상속을 위임으로 전환

위임을 상속으로 전환

복합 리팩토링

상속 구조 정리

상속 구조 정리

절차 코드를 객체로 전환

도메인 로직을 표현과 분리

계층구조 추출

계층구조 추출