반응형
상속은 발등에 불이 떨어져야 비로소 잘못 사용했음을 알아차리는 경우가 많다. 상속은 막강한 도구지만, 잘못된 곳에서 사용되거나 나중에 환경이 변해 문제가 생기기도 한다.
12. 1 매서드 올리기
내용
- 중복된 메서드를 찾아 제거하여 메서드 올리기를 사용한다.
적용
- 메서드들의 본문 코드가 똑같을 때 매서드 올리기를 적용하면 된다.
- 가장 이상하고 복잡한 경우에는 메서드의 본문에서 참조하는 필드들이 서브클래스에만 있는 경우 슈퍼클래스로 올린 후에 메서드를 올려야 한다.
결과
- 중복 코드를 제거하는 것은 중요하기에 효과를 볼 수 있다.
예시
Before
class Employee {...}
class Salesman extends Employee {
get name() {...}
}
class Engineer extends Employee {
get name() {...}
}
After
class Employee {
get name() {...}
}
class Salesman extends Employee {...}
class Engineer extends Employee {...}
12. 2 필드 올리기
내용
- 반대 리팩터링 : 필드 내리기 (12.5절)
- 서브클래스들이 독립적으로 개발 되었거나, 뒤늦게 하나의 계층구조로 리팩터링된 경우라면 일부 기능이 중복되어 있을 수 있다.
적용
- 필드들이 비슷한 방식으로 쓰인다고 판단되면 슈퍼 클래스로 끌어 올려야 한다.
결과
- 필드의 중복 선언 제거
예시
Before
class Employee {...} // Java
class Salesman extends Employee {
private String name;
}
class Engineer extends Employee {
private String name;
}
After
class Employee {
protected String name;
}
class Salesman extends Employee {...}
class Engineer extends Employee {...}
12. 3 생성자 본문 올리기
내용
- 생성자는 다루기 까다롭고, 할 수 있는 일과 호출 순서에 제약이 있기 때문에 제약을 둔다.
적용
- 공통된 생성자가 있을 때 슈퍼클래스로 올린다.
결과
- 생성자의 중복 제거
예시
Before
class Party {...}
class Employee extends Party {
constructor(name, id, monthlyCost) {
super();
this._id = id;
this._name = name;
this._monthlyCost = monthlyCost;
}
}
After
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 메서드 내리기
내용
- 반대 리펙터링 : 메서드 올리기(12.1절)
- 소수의 서브클래스와만 관련된 메서드를 슈퍼클래스에서 서브클래스로 내리는 것
적용
- 하나의 서브클래스에만 관련된 메서드가 있는경우에
결과
- 슈퍼 클래스에 없어도 되는 기능을 내려 용도가 명확해진다.
예시
Before
class Employee {
get quota {...}
}
class Engineer extends Employee {...}
class Salesman extends Employee {...}
After
class Employee {...}
class Engineer extends Employee {...}
class Salesman extends Employee {
get quota {...}
}
12. 5 필드 내리기
내용
- 반대 리펙터링 : 메서드 올리기(12.1절)
- 소수의 서브클래스와만 관련된 메서드를 슈퍼클래스에서 서브클래스로 내리는 것
적용
- 하나의 서브클래스에만 관련된 메서드가 있는경우에
결과
- 슈퍼 클래스에 없어도 되는 기능을 내려 용도가 명확해진다.
예시
Before
class Employee { // Java
private String quota;
}
class Engineer extends Employee {...}
class Salesman extends Employee {...}
After
class Employee {...}
class Engineer extends Employee {...}
class Salesman extends Employee {
protected String quota;
}
12. 6 타입 코드를 서브클래스로 바꾸기
내용
- 비슷한 대상들을 특정 특성에 따라 구분해야 할 때가 자주 있다. 이런 일을 다루는 수단으로 타입 코드 필드가 있다.
- 타입 코드는 프로그래밍 언어에 따라 열거형, 심볼, 문자열, 숫자등으로 표현한다.
- 타입 코드 그 이상이 필요할 때가 있는데 서브클래스를 가리킨다.
적용
- 서브클래스는 조건에 따라 다르게 동작하도록 해주는 다형성을 제공한다. 동작이 달라져야하는 함수가 여러 개일 때 유용하다.
- 또한 특정 타입에서만 의미있는 값을 사용하는 필드나 메서드가 있을 때 발현 된다.
결과
- 다형성을 제공하고 데이터의 관계를 더 명확히 드러낸다.
예시
Before
function createEmployee(name, type) {
return new Employee(name, type);
}
After
function createEmployee(name, type) {
switch (type) {
case "engineer": return new Engineer(name);
case "salesman": return new Salesman(name);
case "manager": return new Manager (name);
}
단점은 대상 클래스를 직접 서브클래싱하는게 간단하지만 특정 유형을 다른 용도로 쓰고 싶을 때 그럴 수 없다. 또한 유형이 불변일 때도 직접 서브클래싱 방식은 이용할 수 없다.
12. 7 서브클래스 제거하기
내용
- 서브 클래싱은 원래 데이터 구조와는 다른 변종을 만들거나 다른 종류에 따라 동작이 달라지게 할 수 있는 유용한 메커니즘이다.
적용
- 소프트웨어 시스템이 성장함에 따라 서브클래스로 만든 변종이 다른 모듈로 이동하거나 완전하 사라짐으로 가치가 바래지기도 한다.
결과
- 불필요한 서브클래스를 슈퍼클래스의 필드로 대체해 제거하여 불필요한 이해도를 없앤다.
예시
Before
class Person {
get genderCode() {return "X";}
}
class Male extends Person {
get genderCode() {return "M";}
}
class Female extends Person {
get genderCode() {return "F";}
}
After
class Person {
get genderCode() {return this._genderCode;}
}
12. 8 슈퍼클래스 추출하기
내용
- 비슷한 일을 수행하는 두 클래스가 보이면 상속 매커니즘을 이용해서 비슷한 부분을 공통의 슈퍼클래스로 옮겨 담을 수 있다.
- 공통 부분이 데이터라면 필드 올리기(12.2절)
- 동작이라면 메서드 올리기(12.1절)
적용
- 상속과 위임으로 중복동작을 해결할지 결정한다.
- 슈퍼클래스로 끌어올리고 싶은 공통 요소를 찾았을 때 수행
결과
- 유사한 클래스를 슈퍼클래스로 추출하여 중복을 제거한다.
예시
Before
class Department {
get totalAnnualCost() {...}
get name() {...}
get headCount() {...}
}
class Employee {
get annualCost() {...}
get name() {...}
get id() {...}
}
After
class Party {
get name() {...}
get annualCost() {...}
}
class Department extends Party {
get annualCost() {...}
get headCount() {...}
}
class Employee extends Party {
get annualCost() {...}
get id() {...}
}
12. 9 계층 합치기
내용
- 클래스 계층 구조를 리팩터링 하다보면 기능들을 위로 올리거나 아래로 하는 일은 다반사
적용
- 클래스와 그 부모가 비슷해져서 독립적으로 존재할 필요가 없는 경우 합친다.
결과
- 불필요 클래스 제거
예시
Before
class Employee {...}
class Salesman extends Employee {...}
After
class Employee {...}
12. 10 서브클래스를 위임으로 바꾸기
내용
- 속한 갈래에 따라 동작이 달라지는 객체는 상속이 자연스럽다.
- 단점은 한 번만 쓸 수 있는 카드가 된다. ('나이대', '소득수준'에 달리 하고싶으면 서브 클래스는 젊은이와 어르신, 부자와 서민이되며 둘다는 되지 않는다)
- 또 다른 단점은 클래스들의 관계를 긴밀하게 결합해 부모를 수정하면 자식 기능을 해친다.
적용
- 처음에는 상속으로 접근을 하고 문제가 생기면 위임으로 상속의 단점을 해결해준다.
결과
- 위임은 상속을 과용하지 않게 하고, 단점 부분을 해결해준다.
예시
Before
class Order {
get daysToShip() {
return this._warehouse.daysToShip;
}
}
class PriorityOrder extends Order {
get daysToShip() {
return this._priorityPlan.daysToShip;
}
}
After
class Order {
get daysToShip() {
return (this._priorityDelegate)
? this._priorityDelegate.daysToShip
: this._warehouse.daysToShip;
}
}
class PriorityOrderDelegate {
get daysToShip() {
return this._priorityPlan.daysToShip
}
}
12. 11 슈퍼클래스를 위임으로 바꾸기
내용
- 객체 지향 프로그래밍에서 상속은 기존 기능을 재활용하는 강력하고 손쉬운 수단이지만, 혼란과 복잡도를 키우는 방식으로 이뤄지기도 한다.
적용
- 기능을 온전히 사용할 수 없고 어울리지 않은 클래스를 상속 받은 경우
- 상속을 먼저 적용하고 문제가 생기면 이 리팩터링을 진행한다.
결과
- 위임을 이용하여 기능의 일부만 빌려오고, 서로 별개인 개념임이 명확해진다.
예시
Before
class List {...}
class Stack extends List {...}
After
class Stack {
constructor() {
this._storage = new List();
}
}
class List {...}
728x90
반응형
'도서' 카테고리의 다른 글
[클린코드] 1~2장 (0) | 2022.02.10 |
---|---|
[리펙터링 2판] 11장 - API 리팩터링 (0) | 2022.02.09 |
[리펙터링 2판] 10장 - 조건부 로직 간소화 (0) | 2022.02.09 |
[리펙터링 2판] 9장 - 데이터 조직화 (0) | 2022.02.08 |
[리펙터링 2판] 8장 - 기능이동 (0) | 2022.01.21 |