암묵적 입력과 암묵적 출력을 없애 액션을 계산으로 만들 수 있다. 그러나 모든 액션들을 없앨 수는 없다. 액션은 필요하니까! 액션을 만드는 것을 피할 수 없으니 액션을 더 좋게 만드는 방법을 정리해보려 한다.
비즈니스 요구 사항은 장바구니에 담긴 제품을 주문할 때 무료 배송인지 확인하는 것이다.
function gets_free_shipping(total, item_price){
return item_price + total >= 20;
}
코드를 보면 비즈니스 요구사항과 맞지 않고 맞는 것처럼 보이게 하는 함수인 것을 알 수 있다.
합계 금액과 제품 가격을 더해서 무료배송 금액과 비교하는 것이 아닌 주문 결과가 무료 배송인지 확인하는 코드를 작성해야 비즈니스 요구사항과 맞는 함수이다.
function calc_total(cart){
var total = 0;
for(var i = 0; i < cart.length; i++) {
var item = cart[i];
total = item.price + total; // 코드 중복
}
return total;
}
게다가 item.price + total
, 장바구니 합계를 계산하는 코드가 중복이다.
중복이 항상 나쁘다고 할 수는 없지만, 코드에서 나는 냄새다. 코드의 냄새 (code smell) 는 나중에 문제가 될 수 있다.
진짜 말 너무 싹바가지없음 코드냄새라니 진짜 누가 저런 말 만들었을까
그래서 함수를 비즈니스 요구사항에 맞게 고쳐주려 한다.
function gets_free_shipping(cart) {
return calc_total(cart) >= 20;
}
calc_total()
함수를 재사용하여 장바구니 안에서의 합계 금액을 구하여 무료 배송 금액과 비교한다.
그러면 비즈니스 요구사항에 맞추어 장바구니에 담긴 물건들의 금액으로 무료 배송인지 계산하면서, 또 코드의 중복도 없앤 함수가 되었다.
인자가 아닌 모든 입력은 암묵적 입력이고, 리턴값이 아닌 모든 출력은 암묵적 출력이다.
어떤 함수에 암묵적 입력과 암묵적 출력이 있다면 다른 컴포넌트와 강하게 연결된 컴포넌트라고 할 수 있다.
또 아무때나 실행할 수 없기 때문에 테스트하기에도 어렵다.
function update_shipping_icons() {
var buttons = get_buy_buttons_dom();
for(var i = 0; i < buttons.length; i++){
var button = buttons[i];
var item = button.item;
var new_cart = add_item(shopping_cart, item.name, item.price);
if(gets_free_shipping(new_cart))
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
코드에서 전역변수인 shopping_cart
를 사용하면서 암묵적 입력과 출력을 이용하고 있다.
shopping_cart
라는 전역변수 대신 인자를 넣어주어 암묵적 입력을 제거해보려 한다.
function update_shipping_icons(cart) {
var buttons = get_buy_buttons_dom();
for(var i = 0; i < buttons.length; i++){
var button = buttons[i];
var item = button.item;
var new_cart = add_item(cart, item.name, item.price);
if(gets_free_shipping(new_cart))
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
update_shipping_icons(shopping_cart);
이제는 함수 안에서 전역 변수인 shopping_cart
를 사용하는 게 아니라 인자로 전달할 수 있다.
함수를 사용하면 관심사를 자연스럽게 분리할 수 있다. 함수는 인자로 넘기는 값과 그 값을 사용하는 방법을 분리한다. 또 가끔은 분리된 것을 합치고 싶을 수도 있다. 하지만 분리한 것은 언제든 쉽게 조합할 수 있기 때문에 잘 분리하면 분리할 수록 좋다. 잘 분리가 된다면, 재사용하기 쉽고, 유지보수하기 쉽고, 테스트하기 쉬워진다.
add_item()
이라는 함수가 있다. 장바구니에 제품을 추가하는 간단한 일을 하는 함수이다. 그런데 정말 간단한걸까?
function add_item(cart, name, price) {
var new_cart = cart.slice(); // 1. 배열 복사
new_cart.push({ // 2. 복사본에 item 추가
name: name, // 3. item 객체 만들기
price: price,
});
return new_cart; // 4. 복사본 리턴
}
어떤 일을 하는지 나누어보면 이렇게 나눌 수 있다.
🤔 음 또 분리할 수 있지 않을까?
1번, 3번, 4번은 값을 바꿀 때 복사하는 카피-온-라이트
를 구현한 부분이기 때문에 함께 두면서 분리하는 것이 좋다.
function make_cart_item(name, price) {
return {
name : name,
price : price,
};
}
function add_item(cart, item) {
var new_cart = cart.slice();
new_cart.push(item);
return new_cart;
}
add_item(shopping_cart, make_cart_item("shoes", 3.45))
암묵적 입력과 암묵적 출력은 인자와 리턴값으로 바꿔 없애주기
설계는 엉켜있는 것을 푸는 것이다. 풀려있는 것은 언제든 합칠 수 있다.
엉켜있는 것을 풀어 각 함수가 하나의 일만 하도록 하면, 개념을 중심으로 쉽게 구성 가능!