쏙쏙 들어오는 함수형 코딩 CH8

Yunes·2023년 10월 2일
0
post-thumbnail

바쁜 현대인을 위한 요약

Ch8 에서는 드디어 계층형 설계를 위해 코드를 계층에 따라 분리하는 방법에 대해 배울수 있습니다. 그중 직접 구현에 대해 자세히 알 수 있습니다.

Ch8 요약

계층형 설계는 소프트웨어를 계층으로 구성하는 기술이다.

같은 계층에 있는 함수는 같은 목적을 가져야 한다.

직접 구현은 계층형 설계를 하기 위한 방법이다.

직접 구현한 코드는 한 단계의 구체화 수준에 관한 문제만 해결한다.

좋은 설계를 고민하고 만든 코드는 읽거나 고치기 쉽다.

계층형 설계는 특정 구체화 단계에 집중할 수 있게 도와준다.

호출 그래프는 구체화 단계에 대한 풍부한 단서를 보여준다.

함수를 추출하면 더 일반적인 함수로 만들 수 있다.

일반적인 함수가 많을수록 재사용하기 좋다.

함수 이름은 의도를 알려주며 함수 본문은 중요한 세부 사항을 알려준다. 함수 이름을 통해 비슷한 목적을 지닌 함수를 함께 묶을 수 있고 함수 본문을 통해 함수가 어떤 계층 구조에 있어야 하는지 알려준다.

함수를 호출하는 화살표가 다양한 길이를 가지고 있다면 직접 구현되어 있지 않다는 신호이다.

계층형 설계 감각을 키우기 위한 입력

  • 함수 본문
    • 길이
    • 복잡성
    • 구체화 단계
    • 함수 호출
    • 프로그래밍 언어의 기능 사용
  • 계층 구조
    • 화살표 길이
    • 응집도
    • 구체화 단계
  • 함수 시그니처
    • 함수명
    • 인자 이름
    • 인잣값
    • 리턴값

계층형 설계 감각을 키우기 위한 출력

  • 조직화
    • 새로운 함수를 어디에 놓을지 결정
    • 함수를 다른 곳으로 이동
  • 구현
    • 구현 바꾸기
    • 함수 추출하기
    • 데이터 구조 바꾸기
  • 변경
    • 새 코드를 작성할 곳 선택하기
    • 적절한 수준의 구체화 단계 결정하기

계층형 설계 패턴

패턴 1. 직접 구현

직접 구현된 함수를 읽일 때 함수 시그니처가 나타내고 있는 문제를 함수 본문에서 적절한 구체화 수준에서 해결해야 한다.

패턴 2. 추상화 벽

인터페이스를 사용하여 세부 구현을 감추고 코드를 작성할 수 있다.

패턴 3. 작은 인터페이스

시스템이 커질수록 비즈니스 개념을 나타내는 중요한 인터페이스는 작고 강력한 동작으로 구성하는 것이 좋다.

패턴 4. 편리한 계층

개발자의 요구를 만족시키면서 비즈니스 문제를 잘 풀 수 있어야 한다.

직접 구현 패턴

함수의 이름, 인자명, 리턴값, 구체화 단계, 함수의 본문 등 앞에서 계층화 설계 감각을 키우기 위한 입력에 여러가지 동류가 있었다. 그것을 감안하고 다음 코드를 봐보자.

function freeTieClip(cart) {
  var hasTie = false
  var hasTieClip = false;
  for(var i = 0; i < cart.length; i++) {
    var item = cart[i];
    if(item.name === "tie")
      hasTie = true;
    if(item.name === "tie clip")
      hasTieClip = true;
  }
  if(hasTie && !hasTieClip) {
    var tieClip = make_item("tie clip", 0);
    return add_item(cart, tieClip);
  }
  return cart;
}

이 함수는 장바구니에 넣은 제품이 tie 혹은 tie clip 이라면 tie clip 을 무료로 주자는 비즈니스 규칙 수준의 함수이다. 이 수준의 함수가 장바구니가 배열이라는 정보는 알 필요가 없다.

이때 책에서는 호출 그래프를 통해 함수 호출에 있어 추상화 단계에 따라 시각화하는 방법에 대해 소개하고 있다.

array index 와 for loop 의 경우 js 자체의 언어 기능이고 make_item, add_item 은 함수를 호출하는 것에 속한다. 높은 단계의 추상화는 더 낮은 단계의 추상화 단계의 코드를 참조하여 동작한다.

이 코드를 개선한다면 같은 추상화 단계의 추상화 계층의 함수를 호출하기 위해 하나의 함수를 추가해줄 수 있다.

function freeTieClip(cart) {
  var hasTie = isInCart(cart, "tie");
  var hasTieClip = isInCart(cart, "tie clip");
  if(hasTie && !hasTieClip) {
    var tieClip = make_item("tie clip", 0);
    return add_item(cart, tieClip);
  }
  return cart;
}

function isInCart(cart, name) {
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      return true;
  }
  return false;
}

이렇게 되면 호출 그래프가 다음과 같이 바뀌게 되고 다른 사람들은 함수에서 비슷한 추상화 계층의 함수를 사용하기에 함수를 읽고 파악하기 쉬워진다.

같은 계층에 있는 함수는 같은 목적을 지녀야 한다.

위의 계층형 설계는 3단계의 줌 레벨로 볼 수 있다.

  1. 전역 줌 레벨 : 호출 그래프 전체
  2. 계층 줌 레벨 : 한 계층과 그 계층에 연결된 바로 아래 계층
  3. 함수 줌 레벨 : 함수 하나와 바로 아래 연결된 함수들

앞선 내용중 직접 구현을 통해 호출 그래프가 같은 추상화 단계의 함수를 호출하도록 했다. 그러나 위의 그림을 보면 함수 줌 레벨에서 볼 때 다른 추상화 단계의 함수를 호출하고 있는 모습을 볼 수 있다. 이는 코드가 정돈되어 있지 않아서이다.

예를 들어 다음의 이름을 기반으로 아이템을 장바구니에서 제거하는 함수의 경우 같은 계층의 함수를 호출하기 위해 새로운 함수 indexOfItem() 함수를 새로 생성하여 사용할 수 있다.

// 원본
function remove_item_by_name(cart, name) {
  var idx = null;
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      idx = i;
  }
  if(idx !== null)
    return removeItems(cart, idx, 1);
  return cart;
}

// 계층형 설계의 직접 구현 적용시
function remove_item_by_name(cart, name) {
  var idx = indexOfItem(cart, name);
  if(idx !== null)
    return removeItems(cart, idx, 1);
  return cart;
}

function indexOfItem(cart, name) {
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      return i;
  }
  return null;
}

더 아래 계층의 함수를 만들수록 더 일반적인 함수를 만들게 된다.

연습하기

비슷한 형태의 함수가 있을때 더 일반적인 함수가 무엇인지, 반환값이 다른 함수가 필요한지 여부를 보고 계층형 설계를 적용하여 코드를 개선할 수 있다.

function isInCart(cart, name) {
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      return true;
  }
  return false;
}

function indexOfItem(cart, name) {
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      return i;
  }
  return null;
}

두 코드는 유사한 형태를 지녔다. 이때 isInCart 의 반환값은 불린값이라 이 함수를 어떻게 사용하는지 알 필요가 없다. indexOfItem 은 반환값이 index 라 배열 형태의 프로그로밍 언어의 기능이 필요하다.

이 점에서 볼때 isInCart 는 더 높은 추상화 단계에서 index, array 에 대해 알 필요가 없듯이 isInCart 가 더 높은 추상화 단계임을 알 수 있어서 이 함수에서 indexOfItem 을 사용하게 코드를 개선할 수 있다.

function isInCart(cart, name) {
  return indexOfItem(cart, name) !== null;
}
    
function indexOfItem(cart, name) {
  for(var i = 0; i < cart.length; i++) {
    if(cart[i].name === name)
      return i;
  }
  return null;
}

그러나 항상 이렇게 설계가 좋아지지 않을 수 있다. 오히려 코드가 더 복잡해지고 긴 화살표가 남아있을 경우 더 좋아졌다고 볼 수 없기도 하다.

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글