✏️8.4 문장을 호출한 곳으로 옮기기 (Move Statements to Callers)

↔️ 문장을 함수로 옮기기

리팩터링 전

emitPhotoData(outStream, person.photo);

function emitPhotoData(outStream, photo){
	outStream.write(photo.title);
	outStream.write(photo.location);
}

리팩터링 후

emitPhotoData(outStream, person.photo);
	outStream.write(photo.location);

function emitPhotoData(outStream, photo){
	outStream.write(photo.title);
}

🧷 배경

함수는 프로그래머가 만드는 추상화 블록이다. 하지만 추상화의 경계를 올바르게 긋기가 어렵다. 함수 관점에서 생각해보면, 초기에는 응집도 높고 한 가지 일만 수행하던 함수가 어느새 둘 이상의 다른 일을 수행하게 바뀔 수도 있다.

여러 곳에서 사용하던 기능이 일부 호출자에게 다르게 동작하도록 바뀌어야 할 때 발생한다. 이때, 문장을 호출한 곳으로 옮기기 리팩터링을 적용하면 달라지는 동작을 호출자로 옮긴 뒤에 필요할 때마다 독립적으로 수정할 수 있다.

🧷 절차

1. 호출자가 한두 개 뿐이고 피호출 함수도 간단한 상황이라면, 피호출 함수의 처음( 혹은 마지막) 줄을 잘라내어 호출자로 복사해 넣는다.

2. 더 복잡한 상황에서는, 이동하지 않길 원하는 모든 문장을 함수로 추출한 다음 검색하기 쉬운 임시 이름을 지어준다.

3. 원래 함수를 인라인 한다.

4. 추출된 함수의 이름을 원래 함수의 이름으로 변경한다.

🧷 예시

function renderPerson(outStream, person) {
  outStream.write(`<p>${person.name}</p>\n`);
  renderPhoto(outStream, persons.photo);
  emitPhotoData(outStream, person.photo);
}

function listRecentPhotos(outStream, photos) {
  photos
    .filter(p => p.data > recentDateCutoff())
    .forEach(p => {
      outStream.write("<div>\n"
      emitPhotoData(outStream, p);
      outStream.write("/</div>\n");
      });
  }

function emitPhotoData(outStream, photo) {
  outStream.write(`<p>제목: ${photo.title}</p>`);
  outStream.write(`<p>날짜: ${photo.date.toDateString()}</p>`);
  outStream.write(`<p>위치: ${photo.location}</p>`);
}

📍 renderPerson()은 그대로 둔 채 listRecentPhotos()가 위치 정보를 다르게 렌더링 하도록 만들어보자.

단순한 상황에서는 renderPerson()의 마지막 줄을 잘라내어 두 호출 코드 아래에 붙여넣으면 끝이다. 까다로운 방법으로 해보자.

  1. emitPhotoData()에 남길 코드를 함수로 추출한다.

function renderPerson(outStream, person) {
  outStream.write(`<p>${person.name}</p>\n`);
  renderPhoto(outStream, persons.photo);
  emitPhotoData(outStream, person.photo);
}

function listRecentPhotos(outStream, photos) {
  photos
    .filter(p => p.data > recentDateCutoff())
    .forEach(p => {
      outStream.write("<div>\n"
      emitPhotoData(outStream, p);
      outStream.write("/</div>\n");
      });
  }

function emitPhotoData(outStream, photo) {
  // 함수 추출
  zztmp(outStream, photo);
  outStream.write(`<p>위치: ${photo.location}</p>`); 
}

function zztmp(outStream, photo) {
  // 바뀌지 않을 코드
  outStream.write(`<p>제목: ${photo.title}</p>`);
  outStream.write(`<p>날짜: ${photo.date.toDateString()}</p>`); 
}
  1. 피호출 함수를 호출자들로 인라인한다.

function renderPerson(outStream, person) {
  outStream.write(`<p>${person.name}</p>\n`);
  renderPhoto(outStream, persons.photo);
  // 분리하기
  zztmp(outStream, person.photo);
  outStream.write(`<p>위치: ${photo.location}</p>`);  // 첫번째 호출 위치로 인라인
}

function listRecentPhotos(outStream, photos) {
  photos
    .filter(p => p.data > recentDateCutoff())
    .forEach(p => {
      outStream.write("<div>\n");
      // 분리하기
      zztmp(outStream, p);
      outStream.write(`<p>위치: ${photo.location}</p>`);  // 두번째 호출 위치로 인라인
      outStream.write("/</div>\n");
      });
  }

function emitPhotoData(outStream, photo) {
  // 함수 추출
  zztmp(outStream, photo);
  outStream.write(`<p>위치: ${photo.location}</p>`); 
}

function zztmp(outStream, photo) {
  // 바뀌지 않을 코드
  outStream.write(`<p>제목: ${photo.title}</p>`);
  outStream.write(`<p>날짜: ${photo.date.toDateString()}</p>`); 
}
  1. 원래 함수를 지우고, zztmp() 이름을 원래 함수의 이름으로 되돌린다.

function renderPerson(outStream, person) {
  outStream.write(`<p>${person.name}</p>\n`);
  renderPhoto(outStream, persons.photo);
  emitPhotoData(outStream, person.photo);
  outStream.write(`<p>위치: ${photo.location}</p>`); 
}

function listRecentPhotos(outStream, photos) {
  photos
    .filter(p => p.data > recentDateCutoff())
    .forEach(p => {
      outStream.write("<div>\n");
      emitPhotoData(outStream, p);
      outStream.write(`<p>위치: ${photo.location}</p>`); 
      outStream.write("/</div>\n");
      });
  }

function emitPhotoData(outStream, photo) {
  outStream.write(`<p>제목: ${photo.title}</p>`);
  outStream.write(`<p>날짜: ${photo.date.toDateString()}</p>`); 
}

Last updated