class List {...}
class Stack extends List {...}
class Stack {
constructor() {
this._storage = new List();
}
}
class List {...}
슈퍼클래스 대신 위임을 사용하는 리팩터링으로 슈퍼클래스의 기능들이 서브클래스에 어울리지 않는다면 그 기능들을 상속을 통해 이용하면 안된다는 신호이다. 이런 경우 상속을 버리고 위임으로 갈아타 객체를 분리하여 혼란과 오류를 피해야 한다.
위임을 이용하면 기능 일부만 빌려오고 서로 별개인 개념이 명확해진다.
class CatalogItem {
constructor(id, title, tags) {
this._id = id;
this._title = title;
this._tags = tags;
}
get id() {return this._id;}
get title() {return this._title;}
hasTag(arg) {return this._tags.includes(arg);}
}
// Scroll 클래스 (CatalogItem을 상속함)
class Scroll extends CatalogItem {
constructor(id, title, tags, dateLastCleaned) {
super(id, title, tags);
this._lastCleaned = dateLastCleaned;
}
needsCleaning(targetDate) {
const threshold = this.hasTag("revered") ? 700 : 1500;
return this.daysSinceLastCleaning(targetDate) > threshold;
}
daysSinceLastCleaning(targetDate) {
return this._lastCleaned.until(targetDate, ChronoUnit.DAYS);
}
}
물리적인 스크롤과 논리적인 카탈로그 아이템에는 차이가 있다. 스크롤은 사본이 여러 개 임에도 카탈로그 아이템은 한 개 뿐이다. 사본 중 하나의 내용을 수정해야 한다면 같은 카테고리 항목의 다른 사본들 모두가 올바르게 수정되는지 확인해야한다.
class CatalogItem {
constructor(id, title, tags) {
this._id = id;
this._title = title;
this._tags = tags;
}
// 2. 전달 메서드 만들기
get id() {return this._catalogItem._id;}
get title() {return this._catalogItem._title;}
hasTag(aString) {return this._catalogItem.hasTag(aString);}
}
// 3. 상속 관계 끊기
class Scroll {
constructor(id, title, tags, dateLastCleaned) {
// 1. 카탈로그 아이템을 참조하는 속성을 만들고 슈퍼클래스의 인스턴스를 새로 하나 만들어 대입한다.
this._catalogItem = new CatalogItem(id, title, tags);
this._lastCleaned = dateLastCleaned;
}
needsCleaning(targetDate) {
const threshold = this.hasTag("revered") ? 700 : 1500;
return this.daysSinceLastCleaning(targetDate) > threshold;
}
daysSinceLastCleaning(targetDate) {
return this._lastCleaned.until(targetDate, ChronoUnit.DAYS);
}
}