[rust] 러스트 코드의 리팩터링 패턴

러스트는 안전하고 병렬 처리가 쉬운 언어이지만, 러스트로 작성된 코드도 계속해서 유지보수되고 리팩터링되어야 합니다. 이러한 과정에서 리팩터링 패턴을 활용하여 코드를 더 깔끔하게 만들 수 있습니다. 이번 블로그에서는 러스트 코드의 리팩터링 패턴 몇 가지를 살펴보겠습니다.

1. Extract Function

기능이 긴 함수를 작게 분해하는 Extract Function 패턴은 코드를 읽기 쉽고 이해하기 쉽게 만들어줍니다. 이 패턴을 활용하면 함수의 목적을 더 명확하게 드러낼 수 있습니다. 또한, 테스트하기도 쉬워지며, 코드를 재사용하기도 용이해집니다.

예시:

fn calculate_total_price(products: Vec<Product>) -> f64 {
    let mut total_price = 0.0;
    for product in products.iter() {
        total_price += product.price;
    }
    return total_price;
}

fn calculate_total_price_refactored(products: Vec<Product>) -> f64 {
    products.iter().map(|product| product.price).sum()
}

2. Replace Iteration with Higher-Order Functions

반복문을 고차 함수로 대체하는 패턴은 코드를 보다 간결하고 함수형 프로그래밍 스타일로 변환할 수 있습니다. 특히 러스트의 반복문 대신 map, filter, fold 등의 고차 함수를 활용하면 코드를 더 간결하게 만들 수 있습니다.

예시:

fn find_even_numbers(numbers: Vec<i32>) -> Vec<i32> {
    let mut even_numbers = vec![];
    for number in numbers.iter() {
        if number % 2 == 0 {
            even_numbers.push(*number);
        }
    }
    return even_numbers;
}

fn find_even_numbers_refactored(numbers: Vec<i32>) -> Vec<i32> {
    numbers.iter().filter(|&number| number % 2 == 0).cloned().collect()
}

3. Introduce Parameter Object

파라미터가 많은 함수의 경우, Introduce Parameter Object 패턴을 활용하여 파라미터를 하나의 객체로 묶을 수 있습니다. 이를 통해 함수의 시그니처를 간결하게 만들고, 관련된 데이터를 논리적으로 묶을 수 있습니다.

예시:

fn calculate_order_total(price: f64, quantity: i32, tax_rate: f64) -> f64 {
    price * quantity as f64 * (1.0 + tax_rate)
}

struct Order {
    price: f64,
    quantity: i32,
    tax_rate: f64,
}

fn calculate_order_total_refactored(order: Order) -> f64 {
    order.price * order.quantity as f64 * (1.0 + order.tax_rate)
}

이처럼, Extract Function, Replace Iteration with Higher-Order Functions, Introduce Parameter Object 등의 리팩터링 패턴을 적절히 활용하여 러스트 코드를 더 깔끔하고 유지보수 가능하게 만들 수 있습니다.