티스토리 뷰

모듈을 잘 분리하는 가장 중요한 기준은

각 모듈이 자신을 제외한 외부에 드러내지 않아야 할 비밀을 얼마나 잘 숨기느냐에 있다.

 

  • 대표적인 데이터 구조 캡슐화
    • 7.1 레코드 캡슐화하기
    • 7.2 컬렉션 캡슐화하기
  • 기본형 데이터 캡슐화
    • 7.3 기본형을 객체로 바꾸기
  • 길어진 함수를 쪼개는
    • 7.4 임시 변수를 질의 함수로 바꾸기
  • 추출하기/인라인의 클래스 버전
    • 7.5 클래스 추출하기
    • 7.6 클래스 인라인하기
  • 클래스 사이의 연결 관계를 숨기는
    • 7.7 위임 숨기기
  • 너무 많이 숨길 경우를 위한 반대 기법
    • 7.8 중개자 제거하기
  • 알고리즘을 통채로 바꿔야 할 때
    • 7.9 알고리즘 교체하기

 

7.1 레코드 캡슐화하기 (Encapsulate Record)

개요

  • 레코드(해시)를 데이터 클래스로 전환하는 리팩터링
  • 해시맵은 다양한 프로그래밍 작업에 유용하지만, 필드를 명확히 알려주지 않는 단점이 있음

적용 시점

  • -

효과

  • 객체를 사용하면 어떻게 저장했는지를 숨긴 채 메서드로 제공할 수 있음
  • 불분명함으로 인해 발생하는 문제를 줄임

before

organization = {name: "Acme Gooseberries", country: "GB"};

after

class Organization {
  constructor(data) {
    this._name = data.name;
    this._country = data.country;
  }
  get name()    {return this._name;}
  set name(arg) {this._name = arg;}
  get country()    {return this._country;}
  set country(arg) {this._country = arg;}
}

 

 

7.2 컬렉션 캡슐화하기 (Encapsulate Collection)

개요

  • 컬렉션 원본을 외부에서 직접 변경할 수 없도록 하는 리팩터링
  • 기본적으로 컬렉션 조작은 메서드로만 제공함
  • 또한, 접근한 컬렉션을 직접 수정하지 못하도록 해야함
    • 복제본을 반환하는 방법
    • 읽기전용으로 제공하는 방법
  • 중요한 점은 코드베이스에서 컬렉션 접근 처리 방식이 통일되어야 함

적용 시점

  • -

효과

  • 모듈 밖에서 컬렉션이 수정되어 발생하는 문제를 예방함 (발생 시 굉장히 찾기 어려움)

before

class Person {              
  get courses() {return this._courses;}
  set courses(aList) {this._courses = aList;}

after

class Person {
  get courses() {return this._courses.slice();}
  addCourse(aCourse)    { ... }
  removeCourse(aCourse) { ... }

 

 

7.3 기본형을 객체로 바꾸기 (Replace Primitive with Object)

개요

  • 단순한 정보를 표현하는 데이터도 개발이 진행되면서 더 이상 간단하지 않게 변하게 되는 경우가 많음

적용 시점

  • 단순한 출력 이상의 기능이 필요해지는 순간 전요 클래스를 정의하는 편

효과

  • 시작은 기본형 데이터를 감싼 것과 큰 차이가 없으나 나중에 특별한 동작이 필요해지면 유용한 도구가 됨

before

orders.filter(o => "high" === o.priority
                || "rush" === o.priority);

after

orders.filter(o => o.priority.higherThan(new Priority("normal")))

 

 

7.4 임시 변수를 질의 함수로 바꾸기 (Replace Temp with Query)

개요

  • 임시 변수를 사용하면 코드가 반복되는 걸 줄이고 값의 의미를 설명할 수도 있어 유용함
  • 임시 변수를 아예 함수로 만들어 사용하는 편이 나을 때도 있음

적용 시점

  • 클래스 안에서 적용할 때 효과가 가장 큼

효과

  • 비슷한 계산 처리를 재 사용할 수 있어 코드 중복이 줄어든다
  • 코드간에 부자연스러운 의존관계나 부수효과를 찾고 제거할 수 있음

before

const basePrice = this._quantity * this._itemPrice;
if (basePrice > 1000)
  return basePrice * 0.95;
else
  return basePrice * 0.98;

after

get basePrice() {this._quantity * this._itemPrice;}

...

if (this.basePrice > 1000)
  return this.basePrice * 0.95;
else
  return this.basePrice * 0.98;

 

 

7.5 클래스 추출하기 (Extract Class)

개요

  • 클래스에 새로운 역할을 덧씌우기 시작하면 굉장히 복잡해지고 어느새 전자레인지로 바짝 익힌 음식처럼 딱딱해진다
  • 메서드와 데이터가 너무 많은 클래스는 이해하기가 쉽지 않으니 잘 살펴보고 적절히 분리하는것이 좋음

적용 시점

  • 일부 데이터와 메서드를 따로 묶을 수 있을 때

효과

  • 클래스를 이해하기 쉬워짐

 

 

7.6 클래스 인라인하기 (Inline Class)

개요

  • 클래스 추출하기를 거꾸로 돌리는 리팩터링
  • 리팩터링 후 클래스에 남은 역할이 거의 없어졌을때 가장 많이 사용하는 클래스로 흡수시키는 작업

적용 시점

  • 클래스에 남은 역할이 거의 없을때

효과

  • 불필요하게 분리된 클래스 제거

 

 

7.7 위임 숨기기 (Hide Delegate)

개요

  • 예컨대 서버 객체의 필드가 가리키는 위임 객체의 메서드를 호출하려면 클라이언트는 이 위임 객체를 알아야 함
  • 이러한 의존성을 없애려면 A 자체에 위임 메서드를 만들어서 위임 객체를 존재함 숨길수 있음

적용 시점

  • 상황에 맞게

효과

  • 위임 객체가 수정 되더라도 클라이언트는 영향을 받지 않음

before

manager = aPerson.department.manager;

after

manager = aPerson.manager;

class Person {
  get manager() {return this.department.manager;}

 

 

7.8 중개자 제거하기 (Remove Middle Man)

개요

  • 단순히 전달만하는 위임 메서드들만 계속해서 추가될 경우 점점 번거로워짐
  • 이런 경우 클래스가 단순 중개자 역할로 전락할 수 있어 차라리 위임 객체를 직접 호출하는게 나아지게 됨

적용 시점

  • 상황에 맞게

효과

  • 클래스가 단순한 중개자 역할로 전락하는걸 막음

before

manager = aPerson.manager;

class Person {
  get manager() {return this.department.manager;}

after

manager = aPerson.department.manager;

 

 

7.9 알고리즘 교체하기 (Substitute Algorithm)

개요

  • 어떤 목적을 달성하는 알고리즘은 여러가지가 있으며 그중에서도 다른 것보다 더 쉬운 방법이 분명히 존재함

적용 시점

  • 기존 코드보다 훨씬 간결한 알고리즘을 찾아낸 경우
  • 내 코드와 똑같은 기능을 제공하는 라이브러리를 찾은 경우
  • 단, 거대하고 복잡한 알고리즘의 경우 교체하기 상당히 어려우니 먼저 메서드를 분리하여 알고리즘을 간소화 해야 함

효과

  • 복잡한 알고리즘을 쉽게 바꾼다

before

function foundPerson(people) {
  for(let i = 0; i < people.length; i++) {
    if (people[i] === "Don") {
      return "Don";
    }
    if (people[i] === "John") {
      return "John";
    }
    if (people[i] === "Kent") {
      return "Kent";
    }
  }
  return "";
}

after

function foundPerson(people) {
  const candidates = ["Don", "John", "Kent"];
  return people.find(p => candidates.includes(p)) || '';
}

 

 

 

참고
http://www.yes24.com/Product/Goods/89649360
https://refactoring.com/catalog

댓글