TotT: 함수 가독성 향상을 위해 추상화를 활용하세요
원문: 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를 참고할 수 있습니다.