Topic Overview

Welcome to the world of refactoring! In this lesson, we're learning about Code Smells, which are patterns in code that hint at potential problems. Our mission is to help you spot these smells and understand how to improve them or, in programming terms, how to refactor them. We'll delve into the concept of code smells, examine different types, and apply real-world code examples to solidify your understanding. Let's get started!

Introduction to Code Smells

Code smells are signs that something could be amiss in our code. You could compare them to an unpleasant smell in a room. But instead of indicating rotten food or a dirty sock, they signal that our code may not be as readable, efficient, or manageable as it could be.

Consider this bit of code:

package main

import "fmt"

func Calculate(quantity int, price int) int {
    return quantity * price
}

func main() {
    total := Calculate(5, 3)
    fmt.Println(total)
}

The function name Calculate is too vague. What exactly does it calculate? For whom? This ambiguity is a sign of a bad naming code smell. Let's see how this and other code smells can be improved!

Duplicate Code

If you notice the same piece of code in more than one place, you may be looking at an example of the Duplicate Code smell. Duplicate code leaves room for errors and bugs. If you need to make a change, you might overlook one instance of duplication.

Here's an example:

totalApplesPrice := quantityApples * priceApple - 5
totalBananasPrice := quantityBananas * priceBanana - 5

This code performs the same operation on different data. Instead of duplicating the operation, we can create a function to handle it:

package main

import "fmt"

func CalculatePrice(quantity, price int) int {
    discount := 5
    return quantity*price - discount
}

func main() {
    totalApplesPrice := CalculatePrice(quantityApples, priceApple)
    totalBananasPrice := CalculatePrice(quantityBananas, priceBanana)
    fmt.Println(totalApplesPrice)
    fmt.Println(totalBananasPrice)
}

With this solution, if we need to change the discount or the formula, we can do so in one place: the CalculatePrice function.

Too Long Method

A function that does too many things or is too long is harder to read and understand, making it a prime candidate for the Too Long Method smell.

Consider this example:

package main

import "fmt"

type Order struct {
    PaymentType string
}

func ProcessOrder(order Order) bool {
    fmt.Println("Processing order...")
    if order.PaymentType == "credit_card" {
        ProcessCreditCardPayment(order)
        SendOrderConfirmationEmail(order)
    } else if order.PaymentType == "paypal" {
        ProcessPaypalPayment(order)
        SendOrderConfirmationEmail(order)
    } else if order.PaymentType == "bank_transfer" {
        ProcessBankTransferPayment(order)
        SendOrderConfirmationEmail(order)
    } else {
        fmt.Println("Unsupported payment type")
        return false
    }
    fmt.Println("Order processed successfully!")
    return true
}

func ProcessCreditCardPayment(order Order) {}
func ProcessPaypalPayment(order Order) {}
func ProcessBankTransferPayment(order Order) {}
func SendOrderConfirmationEmail(order Order) {}

This function handles too many aspects of order processing, suggesting a Too Long Method smell. Breaking down the functionality into smaller, more focused functions is a better approach.

For example, the updated code can look like this:

package main

import "fmt"

type Order struct {
    PaymentType string
}

func ProcessPayment(paymentType string, order Order) bool {
    switch paymentType {
    case "credit_card":
        ProcessCreditCardPayment(order)
    case "paypal":
        ProcessPaypalPayment(order)
    case "bank_transfer":
        ProcessBankTransferPayment(order)
    default:
        fmt.Println("Unsupported payment type")
        return false
    }
    return true
}

func ProcessOrder(order Order) bool {
    fmt.Println("Processing order...")
    if ProcessPayment(order.PaymentType, order) {
        SendOrderConfirmationEmail(order)
        fmt.Println("Order processed successfully!")
        return true
    }
    fmt.Println("Invalid order")
    return false
}

func ProcessCreditCardPayment(order Order) {}
func ProcessPaypalPayment(order Order) {}
func ProcessBankTransferPayment(order Order) {}
func SendOrderConfirmationEmail(order Order) {}
Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal