ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 함수형 프로그램밍(FP) - 더 좋은 액션 만들기1(설계고민, 암묵적 입출력 줄이기)
    CS지식/함수형 프로그래밍 2024. 3. 27. 15:31

    더 좋은 액션 만들기 1

    비지니스 요구 사항과 설계 고민 하기

    요구 사항에 맞춰 더 나은 추상화 단계 선택 

    이전 게시물에서 액션에서 계산으로 리팩토링하는 과정은 단순하고 기계적이었다. 기계적인 리팩토링이 항상 최선의 구조를 만들어주는 것은 아니다. 좋은 구조를 만들기 위해서는 더 많은 고민이 필요하다.

     

    📑비즈니스 요구사항

    "합계 금액과 제품 가격에 대한 무료 배송 여부가 아니고 주문 결과가 무료배송인지 확인해야 한다."

     

    원래코드

    
    function getsFreeShipping(total, itemPrice){
    //total - 현재 총액,
    //itemPrice - 상품을 넣었을때 무료배송이되는지 여부를 알고 싶은 대상
      return itemPrice + total >= 20;
    }

     

    getsFreeShipping() 함수는 비즈니스 요구 사항으로 봤을 때 맞지 않는 부분이 있다. 요구 사항은 장바구니에 담긴 제품을 주문할 때 무료 배송인지 확인하는 것이다. 하지만 위 함수는 장바구니로 무료 배송을 확인하지 않고 제품의 합계와 가격으로 확인하고 있다. 이것은 비즈니스 요구 사항과 맞지 않는인자라고 할 수 있다.

     

    비즈니스 요구 사항을 고려한 함수

    함수의 동작을 바꿨기 때문에 엄밀히 말하면 리팩토링이 아니다.

     

    새 시그니처를 적용한 코드

    function getFreeShipping(cart){
      return calcTotal(cart) >= 20;
    }

     

    함수가 하는 일을 다시 살펴보자 getsFreeShipping()함수는 장바구니 값을 인자로 받아 합계가 20보다 크거나 같은지 알려준다.

     

    바꾼 함수는 합계와 제품가격 대신 장바구니 데이터를 사용한다.장바구니는 전자상거래에서 많이 사용하는 엔티티(entity)타입이기 때문에 비즈니스 요구 상항과 잘 맞는다.


     

    updateShippingIcon() 원래코드 

    function updateShippingIcon(){
      let buttons = getBuyButtonsDom();
      for(const button of buttons){
        if(getsFreeShipping(shoppingCartTotal,button.item))
          button.showFreeShippingIcon();
        else
          button.hideFreeShippingIcon();
      }
    }

     

     

    updateShippingIcon() 새 시그니처를 적용한 코드

    function updateShippingIcons(){
      let buttons = getBuyButtonsDom();
      for(const button of buttons){
      	const{ name, price } = button.item;
       
        //추가할 제품이 들어있는 새 장바구니를 만든다.
        const newCart = addItem(shoppingChart,name,price);
        
        //수정한 함수를 부른다. 
        if(getsFreeShipping(newCart))
          button.showFreeShippIcon();
        else
          button.hideFreeShippingIcon();
      }
    }

     

    getsFreeShipping() 함수가 잘 동작하도록 수정했다. 이제 getsFreeShpping()함구는 장바구니가 무료배송인지 아닌지 알려준다.

     

    🤔 생각해 보기
    방금 바꾼 코드에서 어떤 부분이 가장 인상적인가? 원래 있던 장바구니를 직접 변경하지 않고 복사본을 만들었다. 이런 스타일을 함수형 프로그래밍에서 많이 사용한다. 이 방법을 뭐라고 부를까? 

     


    암묵적 입력과 출력은 적을 수록 좋다.

    파라미터로 전달하는 것이 아닌 모든 입력은 암묵적 입력값이고 리턴값이 아닌 모든 출력은 암묵적 출력이다. 이전 비지니스 요구 사항과 설계사항을 고민하면서 암묵적 입력과 출력이 없는 함수를 작성했고 이 함수를 계산이라고 불렀다.

     

    계산을 만들기위해 암묵적 입력과 출력을 없애는 원칙은 액션에도 적용할 수 있다. 애견에서 모든 암묵적 입력과 출력을 없재지 않더라고 암묵적 입력과 출력을 줄이면 좋다.

    어떤 함수에 암묵적 입력과 출력이 있다면 다른 컴포넌트와 강하게 연결되 컴포턴트라고 할 수 있다. 다른 곳에서 사용할 수 없기 때문에 모듈화 되었다고 할 수 없다. 이런 함수의 동작은 연결된 부분의 동작의 의존한다. 암묵적 입력과 출력을 명시적으로 바꿔 모듈화된 컴포넌트로 만들 수 있다. 납땝하는 대신 쉽게 떼었다 붙일 수 있는 커넥터로 연결된 것이라고 볼 수 있다.

     

    updateShippingIcons()의 암묵적 코드 줄이기 

     

    원래코드

    function calcCartTotal(){
      shoppingCartTotal = calcTotal(shoppingCart);
      setCartTotalDom();
      updateShippingIcons();
      updateTaxDom();
    }
    
    function updateShippingIcons(){
      let buttons = getBuyButtonsDom();
      for(const button of buttons){
      	const{ name, price } = button.item;
       
        const newCart = addItem(shoppingCart,name,price);
        
        if(getsFreeShipping(newCart))
          button.showFreeShippIcon();
        else
          button.hideFreeShippingIcon();
      }
    }

     

     

    명시적 인자로 바꾼 코드

    function calcCartTotal(){
      shoppingCartTotal = calcTotal(shoppingCart);
      setCartTotalDom();
      updateShippingIcons(shippingCart);
      updateTaxDom();
    }
    
    function updateShippingIcons(cart){
      let buttons = getBuyButtonsDom();
      for(const button of buttons){
      	const{ name, price } = button.item;
       
        const newCart = addItem(cart,name,price);
        
        if(getsFreeShipping(newCart))
          button.showFreeShippIcon();
        else
          button.hideFreeShippingIcon();
      }
    }

     

    먼저 전역변수를 사용해서 암묵적 입력이었던 shoppinCart 부분을 인자를 통해 전달 받는 cart로 수정한다. 함수의 시그니처가 달라졌기 때문에 함수를 호출하는 곳도 인자를 넘겨주는 것으로 수정한다.


    이전 게시물에서 다뤘던 기능에 대한 코드 리팩토링 

    https://dodote10.tistory.com/621

     

    원래 코드

    let shoppingCart = [];
    let shoppingCartTotal= 0;
    
    function addItemToCart(name,price){
      shoppingCart = addItem(shoppingCart,name,price);
    
      calcCartTotal(shoppingCart);
    }
    
    function calcCartTotal(cart){
      //카트의 총액 계산
      const total = calcTotal(cart);
      
      //출력:화면의 카트의 총액을 바꾼다.
      setCartTotalDom(total);
      //출력:화면의 세금 액의 바꾼다.
      updateTaxDom(tatal);
      //출력:전역변수에 새로운 총액을 대입
      shoppingCartTotal = total;
    }

     

     

    개선한 코드

    let shoppingCart = [];
    
    
    function addItemToCart(name,price){
      shoppingCart = addItem(shoppingCart,name,price);
    	
       //카트의 총액 계산
      const total = calcTotal(shoppingCart);
      //출력:화면의 카트의 총액을 바꾼다.
      setCartTotalDom(total);
      //출력:화면의 세금 액의 바꾼다.
      updateTaxDom(tatal);
      //출력:전역변수에 새로운 총액을 대입
      shoppingCartTotal = total;
    }

     

    불필요하게 나눠져있던  calcCartTotal()과 addItemToCart() 메서드를 합치고 shoppingCartTotal변수의 경우 addItemToCart()에서만 사용되기 때문에  전역변수를 지우고 필요함수에서만 사용하는 지역변수로 사용한다.


    계산 분류하기

    의미 있는 계층에 대해 알아보기 위해 계산을 분류해보자.

    계산을 더 자세히 들여다보자. 아래는 계산 함수들이다. 어떤 함수가 장바구니 구조를 알아야 하면 C라고 표시하고, 제품에 대한 구조를 알아야 하면 I라고 표기해 보자. 그리고 비지니스 규칙에 대한 함수라면 B라고 표시한다.

    // C, I
    function addItem(cart, name, price){
     const newChart = cart.slice();
     newChart.push({
      name,
      price
     });
     return newCart;
    }
    
    // C, I, B
    function calcTotal(cart){
     //이 함수는 cart구조를 알고
     //MegaMart에서 합계를 결정하는
     //비지니스 규칙도 담고 있다.
     
     let total = 0;
     for(const item of cart){
       total +=item.price;
     }
     return total;
    }
    
    // B
    function gets_free_shipping(cart){
      return calcTotal(cart) >= 20;
    }
    
    // B
    function calcTax(amount){
      return amount * 0.10;
    }

     

    시간이 지날 수록 위와 같이 그룹을 나누는것은 더 명확해질 것이다. 그리고 이렇게 나눈 것은 코드에서 의미 있는 계층이 되기 때문에 기억해두면 좋다. 계층은 엉켜있는 코드는 풀면 자연스럽게 만들어진다.

     

     

    [출처- 쏙쏙 들어오는 함수형 코딩, 저 에릭 노먼드]

    https://jpub.tistory.com/1265

     

    쏙쏙 들어오는 함수형 코딩

    심플한 코드로 복잡한 소프트웨어 길들이기 도서구매 사이트(가나다순) [교보문고] [도서11번가] [알라딘] [예스이십사] [인터파크] [쿠팡]전자책 구매 사이트(가나다순)[교보문고] [구글북스] [리

    jpub.tistory.com

     

    댓글

Designed by Tistory.