리팩터링 (12장 상속 다루기)

박주진·2021년 12월 13일
0

리팩터링

목록 보기
7/7

아래 내용은 리팩터링 2판 내용과 한달한권 읽기 강의를 기반하여 정리한 글입니다.

메서드 올리기

// 전
class Employee {...}

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

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

//후
clas Employee {
	get name() {...}
}

class Salesperson extends Employee {...}
class Engineer extends Employee {...}
  • 상속 구조에 동일한 메서드가 존재한다면 부모에게 올려라.
  • 메서드 올리기시 제거하려는 중복된 메서드가 만약 서브클래스에만 있는 필드를 참조한다면 필드를 먼저 부모로 올려야 한다.

활용될 수 있는 악취들

  • 중복 코드

필드 올리기

// 전
class Employee {...}

class Salesperson extends Employee {
	private String name;
}

class Engineer extends Employee {
	private String name;
}

//후
clas Employee {
	protected String name;
}

class Salesperson extends Employee {...}
class Engineer extends Employee {...}
  • 보통 서브클래스들이 독립적으로 개발되었거나 뒤늦게 계층구조로 리팩터링된 경우에 발생한다.
  • 동적언어 중에는 필드를 클래스 정의에 포함시키지 않는 경우가 있는데 이런경우는 필드를 올리기전 생성자 본문부터 부모 클래스로 올려야 한다.

활용될 수 있는 악취들

  • 중복 코드

생성자 본문 올리기

// 전
class Party {...}

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

//후
clas Party {
	consturctor(name) {
    	this._name = name;
    }
}

class Employee extends Party {
	consturctor(name, id, monthlyCost) {
    	super(name);
        this._id =id;
        this._monthlyCost = monthlyCost;
    }
}
  • 생성자는 일반 메서드와 다르게 호출 순서와 할수 있는 일과 같은 제약사항이 있다.
  • 서브클래스에 중복되는 공통 작업이 서브클래스의 필드들이 추가된 뒤에 이루어 져야 한다면 공통 작업을 추출한 후에 부모 클래스로 옮긴다.
//전
class Employee {
	costructor(name) {...}
    get isPriviledged() {..}
	assignCar() {...}
}

class Manager {
	constructor(name, grade) {
    	super(name);
        this._grade = grade;
        if( this.isPrivileged) this.assignCar(); // 중복되는 공통 작업
    }
    
    get isPrivileged() {
    	return this._grade > 4;	
    }
}

//후
class Employee {
	costructor(name) {...}
    get isPriviledged() {..}
	assignCar() {...}
    finishConstruction() {
    	if(this.isPriviledged) this.assignCar();
    }
}

class Manager {
	constructor(name, grade) {
    	super(name);
        this._grade = grade;
        this.finishConstruction(); // 공통 코드 호출로 변경
    }

}

활용될 수 있는 악취들

  • 중복 제거

메서드내리기

//전
class Employee {
	get quota {...}
}

class Engineer extends Employee {...}
class Salesperson extends Employee {...}

//후
class Employee {...}

class Engineer extends Employee {...}
class Salesperson extends Employee {
	get quota{...}
}
  • 서브클래스에만 필요한 메서드는 슈퍼클래스에서 제거하고 서브클래스에 추가해야 한다.
  • 부모는 공통 로직만 갖는 최소한 형태가 바람직하다.
  • 호출자가 해당 기능을 제공하는 서브클래스를 정확하게 무엇인지 알고 있을때만 적용할 수 있고 그렇지 않다면 다형성을 활용해야 한다.

활용될 수 있는 악취들

  • 상속 포기
  • 추측성 일반화

필드 내리기

//전
class Employee {
	private String quota;
}

class Engineer extends Employee {...}
class Salesperson extends Employee {...}

//후
class Employee {...}

class Engineer extends Employee {...}
class Salesperson extends Employee {
	private String quota;
}
  • 서브클래스에만 필요한 필드는 슈퍼클래스에서 제거하고 서브클래스에 추가해야 한다.
  • 부모는 공통 로직만 갖는 최소한 형태가 바람직하다.

활용될 수 있는 악취들

  • 상속 포기
  • 추측성 일반화

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

//전
function createEmployee(name, type) {
	return new Employee(name, type);
}

//후
function createEmployee(name, type) {
	switch (type) {
    	case "engineer" : return new Engineer(name);
        case "salesperson" : return new Salesperson(anem);
        case "manager" : reutrn new Manager(name);
    	
    }
}
  • 타입에따라 동작이 달라져야 하는 함수가 여러 개일 때 서브클래스를 이용하면 유용하다.
  • 특정 타입에만 의미가 있는 값을 사용하는 필드나 메서드가 있을때 서브 클래스를 이용하면 유용하다.
  • 방법은 대상 코드 즉 Employee객체 자체에 서브클래싱을 적용하는 방법과 타입 코드 자체에 서브클래싱을 적용하는 방법이 있다. 대상 클래스(Employee)에 직접 서브클래싱을 적용하는게 간단하다. 하지만 대상 클래스가 불변이거나 대상 클래스에 다른 서브클래싱을 적용하고 싶다면(상속을 두개의 부모에게 받을수 없음으로) 타입 코드에 서브 클래싱을 적용할 수 있다.

활용될 수 있는 악취들

  • 거대한 클래스
  • 기본형 집착
  • 임시 필드

서브클래스로 제거하기

//전
class Person {
	get genderCode() {return "X";}
}

class Male extends Person {
	get genderCode() {return "M";}
}
class Female extends Person {
	get genderCode() {return "F";}
}

//후
class Person {
	get genderCode() {return this._genederCode;}
}
  • 소프트웨어 시스템이 성장함에 따라 서브클래스가 다른 모듈로 이동하거나 사라지면서 가치가 줄어든다. 가치없는 서브클래스를 이해하느라 에너지를 낭비하기보다는 제거하는게 최선이다.

활용될 수 있는 악취들

  • 성의 없는 요소
  • 추측성 일반화
  • 임시 필드

슈퍼클래스 추출하기

\\전
class Department {
    get totalAnnualCost() {...}
    get name() {...}
    get headCount() {...}
}

class Employee {
    get annualCost() {...}
    get name() {...}
    get id() {...}
}

\\후
class Party {
    get name() {...}
    get annualCost() {...}
}

class Department extends Party {
    get annualCost() {...}
    get headCount() {...}
}

class Employee extends Party {
    get annualCost() {...}
    get id() {...}
}
  • 비슷한 일을 수행하는 두 클래스가 보이면 비슷한 부분을 공통의 슈퍼클래스에 옮겨 담을 수 있다.
  • 구현에 들어가기 앞서 부모/자식 관계를 신중하게 설계하는 경우 보다는 프로그램이 성장하면서 공통 요소가 찾아졌을때 수행하는 경우가 더 많다.

활용될 수 있는 악취들

  • 거대한 클래스
  • 서로 다른 인터페이스의 대안 클래스들
  • 중복 코드

계층 합치기

\\전
class Employee {...}
class Salesperson extends Employee {...}

\\후
class Employee {...}
  • 계층 구조가 진화하면서 서브 클래스가 그 부모와 너무 비슷해져서 독립적으로 존재해야 할 이유가 사라지는 경우 계층을 합쳐라.
  • 서브클래스 제거하기는 자식을 제거하는 것이고 계층 합치기는 부모든 자식이든 둘 중 하나를 제거한다.

활용될 수 있는 악취들

  • 성의 없는 요소

서브클래스를 위임으로 바꾸기

\\전
class Order {
	get daysToShip() {
    	return this._warehouse.daysToShip;
    }

}

class PriorityOrder extends Order {
	get daysToShip() {
    	return this._priorityPlan.daysToShip;
    }
}

\\후
class Order {
	get daysToShip() {
    	return (this._priorityDelegate)
        ? this._priorityDelegate.dyasToShip
        : this._warehouse.daysToShip;
   
    }
}

class PriorityOrderDelegate {
	get daysToShip() {
    	return this._priorityPlan.daysToShip;
    }
}
  • 상속은 두개의 명확한 단점이 있다. 첫째, 한번만 사용할 수 있다. 둘째, 클래스들의 관계가 아주 긴밀하게 결합된다. 위임은 두 문제 모두 해결해준다.

활용될 수 있는 악취들

  • 내부자 거래
  • 상속 포기
  • 중개자

슈퍼클래스를 위임으로 바꾸기

\\전
class List {...}
class Stack extends List {...}

\\후
class Stack {
	constructor() {
    	this._storage = new List();
    }   
}

class List{..}
  • 슈퍼 클래스의 기능들이 서브클래스에 이울리지 않는다면 그 기능을 상속하지 말고 위임을 사용하라. 잘못된 상속 예)java 의 stack 클래스
  • 상위 타입의 모든 메서드가 하위 타입에도 적용되고, 하위 타입의 모든 인스턴스가 상위 타입의 인스턴스도 되는 경우와 같이 적합하다면 상속은 간단하고 효과적이므로 사용해라. 하지만 상황이 변해 더는 최선에 방법이 아닐게 되면 위임으로 변경하라.

활용될 수 있는 악취들

  • 내부자 거래
  • 상속 포기
  • 중개자

2개의 댓글

comment-user-thumbnail
2021년 12월 22일

한달한권 리팩터링 강의 구매 고민 중인데 혹시 만족하시나요?

1개의 답글