본문 바로가기
핌팩토링

12. 상속 다루기

by AsCE_hyunseung 2022. 6. 19.

12.1 메서드 올리기

배경

  • 중복된 두 메서드가 미래에는 냄새나는 쓰레기가 될 수 있다.
  • 무언가 중복되었다는 것은 한쪽의 변경이 다른 쪽에는 반영되지 않을 수 있다는 위험을 항상 수반한다.
  • 메서드 올리기를 적용하기 가장 쉬운 상황은 메서드들의 본문 코드가 똑같을 때이다.

절차

  1. 똑같이 동작하는 메서드인지 면밀히 살펴본다
  2. 메서드 안에서 호출하는 다른 메서드와 참조하는 필드들을 슈퍼클래스에서도 호출하고 참조할 수 있는지 확인한다.
  3. 메서드 시그니처가 다르다면 함수 선언 바꾸기로 슈퍼클래스에서 사용하고 싶은 형태로 통일한다.
  4. 슈퍼클래스에 새로운 메서드를 생성하고, 대상 메서드의 코드를 복사해넣는다.
  5. 서브클래스 중 하나의 메서드를 제거한다.
  6. 모든 서브 클래스의 메서드가 없어질 때까지 다른 서브클래스의 메서드를 하나씩 제거한다.

예시

class Employee {
  ...
}

class SalesPerson extends Employee {
  get name() {
    ...
  }
}

class Engineer extends Employee {
  get name() {
    ...
  }
}
------------------------------------
class Employee {
  get name() {
    ...
  }
}

class SalesPerson extends Employee {
  ...
}

class Engineer extends Employee {
  ...
}

12.2 필드 올리기

배경

  • 어떤 일이 벌어지는지를 알아내려면 필드들이 어떻게 이용되는지 분석해봐야 한다.
    • 분석 결과 필드들이 비슷한 방식으로 쓰인다고 판단되면 슈퍼클래스로 끌어올리자
  • 필드 올리기를 하면 두가지의 장점이 있다.
    • 데이터 중복 선언을 없앨 수 있다.
    • 해당 필드를 사용하는 동작을 서브클래스에서 슈퍼클래스로 옮길 수 있다.

절차

  1. 후보 필드들을 사용하는 곳 모두가 그 필드를 똑같은 방시긍로 사용하는지 살핀다.
  2. 필드들의 이름이 각각 다르다면 똑같은 이름으로 바꾼다.
  3. 슈퍼클래스에 새로운 필드를 생성한다.
  4. 서브클래스의 필드들을 제거한다.

예시

class Employee {
  ...
}

class SalesPerson extends Employee {
  name=""
}

class Engineer extends Employee {
  name=""
}
------------------------------------
class Employee {
  name=""
}

class SalesPerson extends Employee {
  ...
}

class Engineer extends Employee {
  ...
}

12.3 생성자 본문 올리기

배경

  • 생성자는 다루기 까다로워서 생성자에서 하는 일에 제약을 두자
    • 할 수 있는 일과 호출 순서에 제약이 있다.

절차

  1. 슈퍼클래스에 생성자가 없다면 하나 정의한다.
  2. 문장 슬라이드하기로 공통 문장 모두를 super() 호출 직후로 옮긴다.
  3. 공통 코드를 슈퍼클래스에 추가하고 서브클래스들에서는 제거한다. 생성자 매개변수 중 공통 코드에서 참조하는 값들을 모두 super()로 건넨다.
  4. 생성자 시작 부분으로 옮길 수 없는 공통 코드에는 함수 추출하기와 메서드 올리기를 차례로 적용한다.

예시

class Party {
	...
}

class Employee extends Party {
  constructor(name, id, monthlyCost) {
    super();
    this._id = id;
    this._name = name;
    this._monthlyCost = monthlyCost
  }
}
--------------------------
class Party {
  constructor(name) {
    this._name = name
  }
}

class Employee extends Party {
  constructor(name, id, monthlyCost) {
    super(name);
    this._id = id;
    this._monthlyCost = monthlyCost
  }
}

12.4 메서드 내리기

배경

  • 특정 서브클래스 하나만 관련된 메서드는 슈퍼클래스에서 제거하고 해당 서브클래스에 추가하는 편이 깔끔하다.
  • 이 리팩터링은 해당 기능을 제공하는 서브클래스가 정확히 무엇인지를 호출자가 알고 있을 때만 적용할 수 있다.

절차

  1. 대상 메서드를 모든 서브 클래스에 복사한다.
  2. 슈퍼 클래스에서 그 메서드를 제거한다.
  3. 이 메서드를 사용하지 않는 모든 서브클래스에서 제거한다.

예시

class Employee {
  get quota {
    ...
  }
}

class SalesPerson extends Employee {
  ...
}

class Engineer extends Employee {
  ...
}
------------------------------------
class Employee {
  ...
}

class SalesPerson extends Employee {
  ...
}

class Engineer extends Employee {
  get quota {
    ...
  }
}

12.5 필드 내리기

배경

  • 서브클래스 하나에서만 사용하는 필드는 해당 서브클래스로 옮긴다.

절차

  1. 대상 필드를 모든 서브클래스에 정의한다
  2. 서브클래스에서 그 필드를 제거한다.
  3. 이 필드를 사용하지 않는 모든 서브클래스에서 제거한다.

예시

class Employee {
  quota=""
}

class SalesPerson extends Employee {
  ...
}

class Engineer extends Employee {
  ...
}
------------------------------------
class Employee {
  ...
}

class SalesPerson extends Employee {
  quota=""
}

class Engineer extends Employee {
  ...
}

12.6 타입 코드를 서브클래스로 바꾸기

배경

  • 타입 코드만으로도 특별히 불편한 상황은 별로 없지만, 그 이상의 무언가가 필요할 떄가 있다.
  • 그 이상은 서브클래스를 가리킨다. 서브클래스는 두 가지 면에서 특히 매력적이다
    1. 조건에 따라 다르게 동작하도록 해주는 다형성을 제공한다.
    2. 특정 타입에서만 의미가 있는 값을 사용하는 필드나 메서드가 있을 때 발현된다.
  • 이번 리팩터링은 대상 클래스에 직접 적용할지, 아니면 타입 코드 자체에 적용할지를 고민해야한다.

절차

  1. 타입 코드 필드를 자가 캡슐화한다.
  2. 타입 코드 값 하나를 선택하여 그 값에 해당하는 서브클래스를 만든다. 타입 코드 게터 메서드를 오버라이드하여 해당 타입 코드의 리터럴 값을 반환하게 한다.
  3. 매개변수로 받은 타입 코드와 방금 만든 서브클래스를 매핑하는 전체 로직을 만든다.
  4. 타입 코드 값 각각에 대해 서브클래스 생성과 선택 로직 추가를 반복한다.
  5. 타입 코드 필드를 제거한다.
  6. 타입 코드 접근자를 이용하는 메서드 모두에 메서드 내리기와 조건부 로직을 다형성으로 바꾸기를 적용한다.

예시

'핌팩토링' 카테고리의 다른 글

11. API 리팩터링  (0) 2022.06.19
10. 조건문 로직 간소화  (0) 2022.04.28
9. 데이터 조직화  (0) 2022.04.28
8. 기능 이동  (0) 2022.04.27
7. 캡슐화  (0) 2022.04.27