핌팩토링
12. 상속 다루기
by AsCE_hyunseung
2022. 6. 19.
12.1 메서드 올리기
배경
- 중복된 두 메서드가 미래에는 냄새나는 쓰레기가 될 수 있다.
- 무언가 중복되었다는 것은 한쪽의 변경이 다른 쪽에는 반영되지 않을 수 있다는 위험을 항상 수반한다.
- 메서드 올리기를 적용하기 가장 쉬운 상황은 메서드들의 본문 코드가 똑같을 때이다.
절차
- 똑같이 동작하는 메서드인지 면밀히 살펴본다
- 메서드 안에서 호출하는 다른 메서드와 참조하는 필드들을 슈퍼클래스에서도 호출하고 참조할 수 있는지 확인한다.
- 메서드 시그니처가 다르다면 함수 선언 바꾸기로 슈퍼클래스에서 사용하고 싶은 형태로 통일한다.
- 슈퍼클래스에 새로운 메서드를 생성하고, 대상 메서드의 코드를 복사해넣는다.
- 서브클래스 중 하나의 메서드를 제거한다.
- 모든 서브 클래스의 메서드가 없어질 때까지 다른 서브클래스의 메서드를 하나씩 제거한다.
예시
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 필드 올리기
배경
- 어떤 일이 벌어지는지를 알아내려면 필드들이 어떻게 이용되는지 분석해봐야 한다.
- 분석 결과 필드들이 비슷한 방식으로 쓰인다고 판단되면 슈퍼클래스로 끌어올리자
- 필드 올리기를 하면 두가지의 장점이 있다.
- 데이터 중복 선언을 없앨 수 있다.
- 해당 필드를 사용하는 동작을 서브클래스에서 슈퍼클래스로 옮길 수 있다.
절차
- 후보 필드들을 사용하는 곳 모두가 그 필드를 똑같은 방시긍로 사용하는지 살핀다.
- 필드들의 이름이 각각 다르다면 똑같은 이름으로 바꾼다.
- 슈퍼클래스에 새로운 필드를 생성한다.
- 서브클래스의 필드들을 제거한다.
예시
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 생성자 본문 올리기
배경
- 생성자는 다루기 까다로워서 생성자에서 하는 일에 제약을 두자
절차
- 슈퍼클래스에 생성자가 없다면 하나 정의한다.
- 문장 슬라이드하기로 공통 문장 모두를 super() 호출 직후로 옮긴다.
- 공통 코드를 슈퍼클래스에 추가하고 서브클래스들에서는 제거한다. 생성자 매개변수 중 공통 코드에서 참조하는 값들을 모두 super()로 건넨다.
- 생성자 시작 부분으로 옮길 수 없는 공통 코드에는 함수 추출하기와 메서드 올리기를 차례로 적용한다.
예시
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 메서드 내리기
배경
- 특정 서브클래스 하나만 관련된 메서드는 슈퍼클래스에서 제거하고 해당 서브클래스에 추가하는 편이 깔끔하다.
- 이 리팩터링은 해당 기능을 제공하는 서브클래스가 정확히 무엇인지를 호출자가 알고 있을 때만 적용할 수 있다.
절차
- 대상 메서드를 모든 서브 클래스에 복사한다.
- 슈퍼 클래스에서 그 메서드를 제거한다.
- 이 메서드를 사용하지 않는 모든 서브클래스에서 제거한다.
예시
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 필드 내리기
배경
- 서브클래스 하나에서만 사용하는 필드는 해당 서브클래스로 옮긴다.
절차
- 대상 필드를 모든 서브클래스에 정의한다
- 서브클래스에서 그 필드를 제거한다.
- 이 필드를 사용하지 않는 모든 서브클래스에서 제거한다.
예시
class Employee {
quota=""
}
class SalesPerson extends Employee {
...
}
class Engineer extends Employee {
...
}
------------------------------------
class Employee {
...
}
class SalesPerson extends Employee {
quota=""
}
class Engineer extends Employee {
...
}
12.6 타입 코드를 서브클래스로 바꾸기
배경
- 타입 코드만으로도 특별히 불편한 상황은 별로 없지만, 그 이상의 무언가가 필요할 떄가 있다.
- 그 이상은 서브클래스를 가리킨다. 서브클래스는 두 가지 면에서 특히 매력적이다
- 조건에 따라 다르게 동작하도록 해주는 다형성을 제공한다.
- 특정 타입에서만 의미가 있는 값을 사용하는 필드나 메서드가 있을 때 발현된다.
- 이번 리팩터링은 대상 클래스에 직접 적용할지, 아니면 타입 코드 자체에 적용할지를 고민해야한다.
절차
- 타입 코드 필드를 자가 캡슐화한다.
- 타입 코드 값 하나를 선택하여 그 값에 해당하는 서브클래스를 만든다. 타입 코드 게터 메서드를 오버라이드하여 해당 타입 코드의 리터럴 값을 반환하게 한다.
- 매개변수로 받은 타입 코드와 방금 만든 서브클래스를 매핑하는 전체 로직을 만든다.
- 타입 코드 값 각각에 대해 서브클래스 생성과 선택 로직 추가를 반복한다.
- 타입 코드 필드를 제거한다.
- 타입 코드 접근자를 이용하는 메서드 모두에 메서드 내리기와 조건부 로직을 다형성으로 바꾸기를 적용한다.
예시