원문: https://testing.googleblog.com/2023/09/use-abstraction-to-improve-function.html (Translated by Google Gemini)


아래에 있는 두 가지 createPizza 함수 버전 중 어느 쪽이 이해하기 더 쉬울까요?

A. 추상화 수준이 혼재된 버전 (나쁜 예)#

func createPizza(order *Order) *Pizza {
    pizza := &Pizza{Base: order.Size, Sauce: order.Sauce, Cheese: "Mozzarella"}
    if order.kind == "Veg" {
        pizza.Toppings = vegToppings
    } else if order.kind == "Meat" {
        pizza.Toppings = meatToppings
    }

    oven := oven.New()
    if oven.Temp != cookingTemp {
        for (oven.Temp < cookingTemp) {
            time.Sleep(checkOvenInterval)
            oven.Temp = getOvenTemp(oven)
        }
    }

    if !pizza.Baked {
        oven.Insert(pizza)
        time.Sleep(cookTime)
        oven.Remove(pizza)
        pizza.Baked = true
    }

    box := box.New()
    pizza.Boxed = box.PutIn(pizza)
    pizza.Sliced = box.SlicePizza(order.Size)
    pizza.Ready = box.Close()
    return pizza
}

B. 일관된 추상화 수준을 가진 버전 (좋은 예)#

func createPizza(order *Order) *Pizza {
    pizza := prepare(order)
    bake(pizza)
    box(pizza)
    return pizza
}

func prepare(order *Order) *Pizza {
    pizza := &Pizza{Base: order.Size, Sauce: order.Sauce, Cheese: "Mozzarella"}
    addToppings(pizza, order.kind)
    return pizza
}

func addToppings(pizza *Pizza, kind string) {
    if kind == "Veg" {
        pizza.Toppings = vegToppings
    } else if kind == "Meat" {
        pizza.Toppings = meatToppings
    }
}

func bake(pizza *Pizza) {
    oven := oven.New()
    heatOven(oven)
    bakePizza(pizza, oven)
}

func heatOven(oven *Oven) {  }

func bakePizza(pizza *Pizza, oven *Oven) {  }

func box(pizza *Pizza) {  }

추상화 수준을 일치시켜야 하는 이유#

여러분은 아마 오른쪽(좋은 예)이 더 쉽다고 답했을 것입니다. 하지만 그 이유는 무엇일까요?

왼쪽 버전은 여러 추상화 수준을 함께 혼합하고 있습니다. 여기에는 저수준 구현 세부사항(예: 오븐을 예열하는 방법), 중간 수준 함수(예: 피자를 굽는 방법), 그리고 고수준 추상화(예: 피자를 준비하고, 굽고, 포장하는 것)가 모두 섞여 있습니다.

오른쪽 버전이 더 이해하기 쉬운 이유는 함수들이 일관된 추상화 수준을 가지고 있기 때문입니다. 이는 코드 로직에 대한 하향식 서술(top-down narrative) 을 제공합니다. createPizza는 고수준 함수로서, 준비, 굽기, 포장 단계를 직관적인 이름을 가진 더 낮은 수준의 특화된 함수들에게 위임합니다. 그 함수들은 다시 다른 함수 호출이 필요하지 않은 구현 세부사항을 다루는 함수에 도달할 때까지 그들 자신의 더 낮은 수준의 특화된 함수들(예: heatOven)에 작업을 위임합니다.

하나의 함수 안에 여러 다른 추상화 레이어를 섞는 것을 피하세요. 함수들을 동일한 추상화 수준으로 중첩시켜 서술적인 구조를 제공하십시오. 이렇게 코드가 스스로를 설명하는(self-documenting) 스타일은 따라가기, 디버깅하기, 그리고 재사용하기에 더 간단합니다.

이 주제에 대해 더 자세히 알아보려면 Robert C. Martin이 저술한 Clean Code를 참고할 수 있습니다.