[리팩터링 2판] 7. 캡슐화

해당 내용은 리팩터링 2판을 읽고 개인적으로 정리 및 개인적으로 느낀 내용입니다.

레코드 캡슐화하기

  • 간단한 레코드 예제
// 캡슐화 전
class Oraganization {
	constructor(data) {
    	this._name = data.name;
        this._country = data.country;
    }
    get name() { return this._name;}
    set name(arg) { this._name = arg; }
} 

// 캡슐화 이후
class Oraganization {
	constructor(data) {
    	this._data = data;
    }
    get name() { return this._data.name;}
    set name(arg) { this._data._name = arg; }
} 
  • 중복된 레코드 예제
  1. 내부 객체를 복사해서 사용
    -> 데이터의 구조가 클수록 비용이 올라간다.
    -> 클라이언트가 원본을 수정한다고 착각할 가능성이 있다.(해결: readonly 제공 || 복제본 동결)
  2. 레코드 캡슐화를 재귀적으로 실행

컬렉션 캡슐화하기

1. 변수 캡슐화
2. 컬렉션에 원소를 추가/제거하는 함수를 추가
3. 정적 검사 수행
4. 컬렉션을 참조하는 부분을 모두 찾고 컬렉션의 변경자를 호출하는 코드가 모두 앞에서 추가한 추가/제거 함수를 호출하도록 수정한다. =>** setter가 불필요**해진다.
5. 컬렉션의 getter를 수정하여 원본 내용을 수정할 수 없는 readonly proxy 또는 복제본을 반환하도록 한다.

기본형 -> 객체로 변경

  1. 변수 캡슐화

    get priority() { return this._priority;}
    set priority(aString) { return this._priority = aString;}
  2. class 생성

    class Priority {
    	constructor(value) { this._value = value;};
        toString() { return this._value; } // getter
        
    }
  3. 정적 검사 수행

  4. getter & setter 수정

    get priority() { return this.priority.toString();}
    set priority(aString) { this._priority = new Priority(aString);}
  5. 함수 변경

class Order {
	get priorityString() { return this._priority.toString(); }
    set priority(aString) { this._priority = new Priority(aString);}
}

임시 변수를 질의 함수로 변경

// 리팩토링 전
constructor(quantity, item) {
	this._quantity = quantity;
    this._item = item;
}
get price() {
	var basePrice = this._quantity * this._item.price; 
    var discountFactor = 0.98;
    
    if (basePrice > 1000) discountFactor -= 0.03;
    return basePrice * discountFactor;
}

// 리팩토링 후
get price () {
	return this.basePrice * this.discountFactor;

클래스 추출 (<-> 클래스 인라인)

특정 데이터, 메소드를 일부 제거해도 논리적으로 문제가 없다면 클래스를 분리할 것.

// 변경 전
class Person {
	get name() { return this._name; }
    set name(arg) { this._name = arg; }
    get telephoneNumber() { return `(${this.officeAreaCode});
    get officeAreaCode() { return this_officeAreaCode; }
    get officeNumber() { return this._officeNumber; }
   	set officeNumber(arg) { this_officeNumber = arg; }    
}
// 변경 후
class Person {
	
}

class TelephoneNumber {
	get areaCode() { return this._areaCode; }
    set areaCode(arg) { this.areaCode = arg; }
    get number() { return this._number; }
    set number(arg) { this.number = arg; }
    toString() { return `(${this.areaCode}) ${this.number}`;}
}

class Person {
	get officeAreaCode() {..}
    set officeAreaCode(arg) {..}
    get officeNumber() {..}
    set officeNumber(arg) {..}
    
    get telephoneNumber() {return this._telephoneNumber.toString();
}

클래스 인라인 (<-> 클래스 추출하기)

클래스의 남은 역할이 거의 없는 경우 가장 많이 사용하는 클래스로 흡수시키는 방법

EX) TrackingInformation.class < Shipment.class 흡수

위임 숨기기 (<-> 중재자 제거하기)

ex) 어떤 사람이 속한 부서의 관리자를 알고싶다.
-> 부서 클래스는 관리자 정보를 제공한다.

// 위임 숨기기 전
class Person {
	get name() {}
    set name(arg) {}
    get department() {}
    set department(arg) {}
}

class Department {
  ...,
  get manager() {}
  set manager(arg) {}	
}

manager = aPerson.department.manager;

// 위임 숨기기
class Person {
	get name() {}
    set name(arg) {}
    get manager() {return this._department.manager;}
}

manager = aPerson.manager;

중재자 제거하기 (<-> 위임 숨기기)

// Person.class가 단지 위임용으로만 사용됨.
class Person {
	get manager() { return this._department.manager;}
}

manager = aPerson.manager;

// 중재자 제거 후
manager = aPerson.department.manager;

알고리즘 교체하기

-> 일반적으로 내가 생각하고 있던 리팩터링의 개념

 // 리팩터링 전
 function foundPerson(people) {
 	for(let i=0; i<people.length; i++) {
    	if (people[i] === "Don") {
        	return "Don";
        }
        ...
    }
 }
 // 리팩터링 후
 function foundPerson(people) {
 	const candidates = ["Don", "..", ".."];
    return people.find(p => p.candidates.includes(p)) || "";
 }

좋은 웹페이지 즐겨찾기