Java 리펙토링 #2. Duplicated Code

Koboolean·2024년 6월 28일

Java

목록 보기
3/3
post-thumbnail

참고사항

해당 포스팅은 인프런 백기선님의 '리팩토링'을 학습 후 정리한 내용입니다.

Sec1. 중복코드의 단점

  • 코드를 변경할 때, 동일한 모든 곳의 코드를 변경해야한다.
  • 해당 코드들이 비슷한 코드들인지, 완전히 동일한 코드들인지 주의깊게 확인하여야 한다.

Sec2. 사용할 수 있는 리펙토링 기술

  1. 함수 추출하기(Extract Function)
  2. 코드 분리하기(Slide Statements)
  3. 메소드 올리기(Pull Up Method)

Sec3. 함수 추출하기

  • 동일한 코드를 여러 메소드에서 사용하는 경우 할 수 있는 리펙토링 기술이다.

  • "의도"와 "구현"을 분리한다.

메서드 추출을 통한 분리

  • 무슨일을 하는 코드인지 알아내려고 노력해야 하는 코드(의도가 모호한 코드) 해당 코드를 함수로 분리하며, 함수 이름은 "무슨 일을 하는지"로 표현할 수 있다.

    • 코드를 읽는 사람 입장에서는 함수가 하는 일을 파악하기 위해 왔다 갔다 해야 하므로 부담은 증가할 수 있다.
    • 다행히 함수 호출부와 선언부 사이를 빠르게 이동하거나 호출과 선언을 동시에 보여주는 개발 환경을 활용하면 이 부담이 줄어들지만, 짧은 함수로 구성된 코드를 이해하기 쉽게 만드는 가장 확실한 방법은 좋은 이름이다.
    • 함수 이름을 잘 지어두면 본문 코드를 볼 이유가 사라진다. 그러기 위해서는 훨씬 적극적으로 함수를 쪼개야 한다.
  • 한 줄 짜리 메소드도 의도를 분명히 할 수 있다면 추출하는 것이 좋다.

  • 거대한 함수 안에 들어있는 주석은 추출한 함수를 찾는 데 있어 좋은 단서가 될 수 있다.

    • 함수 본문에는 원래 주석으로 설명하려던 코드가 담기고 함수 이름은 동작 방식이 아닌 '의도'가 드러나게 짓는다.
    • 원래 코드보다 길어지더라도 함수로 뽑는다.
    • 함수 이름에 코드의 목적을 드러내야 한다. 여기서 핵심은 함수의 길이가 아닌, 함수의 목적(의도)과 구현 코드의 괴리가 얼마나 큰가다.
    • 즉, '무엇을 하는지'를 코드가 잘 설명해주지 못할수록 함수로 만드는 게 유리하다.

Sec4. 코드 분리하기

  • 코드가 비슷하게 생겼지만 완전히 같지는 않은 경우 해당 리펙토링 기술을 적용한다.
  • 실제로 관련있는 코드끼리 묶여있는 코드 묶음이 좀더 직관적으로 확인이 가능하다.
    • 함수에서 사용할 변수를 상단에 미리 정의하기 보다는, 해당 변수를 사용하는 코드 바로 위에 선언하자.
    • 관련있는 코드 끼리 묶은 다음, 함수 추출하기를 사용해서 좀 더 깔끔하게 분리가 가능하다.
    • Intellij에서 줄을 아래로 이동하는 방법은 Alt + Shift + Down 이다.
    • 줄을 아래, 위로 움직여 관련있는 코드끼리 묶은 후 메소드 추출하기를 이용하여 관련 메소드별로 추출하자.

Sec5. 메소드 올리기

  • 여러 하위 클래스에 동일한 코드들이 존재한다면, 다음의 리펙토링 기술을 적용해야 할 차례이다.

  • 중복된 코드들은 당장 잘 동작하더라도, 미래에 버그를 만들어 낼 빌미를 제공한다.

    • A에서 코드를 고치더라도 B에는 반영되지 않아 예기치 못한 오류를 범할 수 있다.
  • 여러 하위 클래스에 동일한 코드가 있다면, 손쉽게 이 방법을 적용할 수 있다.

  • 비슷하지만 일부 값이 다른 경우, "함수 매개변수화하기(Parameterize Function)" 리펙토링을 적용 후 해당 방법을 사용할 수 있다.

/** 함수매개변수화 하기 전 */
private double tenPercentRaise(Map aPerson) {
  aPerson.salary = aPerson.salary.multiply(1.1);
  return aPerson;
}
private double fivePercentRaise(Map aPerson) {
  return aPerson.salary.multiply(1.05);
}
/** 함수매개변수화 한 후 */
private double raise(aPerson, factor) {
  return aPerson.salary.multiply(1 + factor);
}
  • 하위 클래스에 있는 코드가 상위 클래스가 아닌 하위클래스 기능에 의존하고 있다고 한다면, "필드 올리기(Pull Up Field)"를 적용한 후 이방법을 적용할 수 있다.
/** 필드올리기 적용 전 */
class Employee {}

class Person extends Employee {
  private name;
}

class Developer extends Employee {
  private name;
}
/** 필드올리기 적용 후 */
class Employee {
  protected name;
}

class SalesPerson extends Employee {}
class Engineer extends Employee {}
  • 두 메소드가 비슷한 절차를 따르고 있다면, “템플릿 메소드 패턴 (Template Method Pattern)” 적용을 고려할 수 있다.
profile
WEB 개발자의 끄적끄적 개발일기

0개의 댓글