Skip to content

Latest commit

 

History

History
86 lines (62 loc) · 3.13 KB

File metadata and controls

86 lines (62 loc) · 3.13 KB

Refactoring

Definition

Refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior. It's a disciplined technique for improving the design of existing code. The primary goal of refactoring is to make code easier to understand, cheaper to modify, and less prone to bugs.

Why Refactor?

  • Improve Code Design: Clean up messy, complex, or poorly structured code.
  • Enhance Readability: Make code easier for developers to read and comprehend.
  • Reduce Technical Debt: Pay down technical debt to keep the codebase healthy.
  • Facilitate New Feature Development: A clean codebase is easier to extend.
  • Reduce Bugs: Simplify logic and improve clarity to prevent future errors.

When to Refactor?

  • The "Boy Scout Rule": Always leave the code cleaner than you found it.
  • Before Adding a New Feature: Prepare the code for the new functionality.
  • When Fixing a Bug: Clean up the problematic code.
  • During Code Reviews: Identify refactoring opportunities.

Common Code Smells

Code smells are symptoms of poor design that may indicate the need for refactoring.

  • Long Method: A method that is too long and does too many things.
  • Large Class: A class that has too many responsibilities.
  • Duplicate Code: The same code appears in multiple places.
  • Primitive Obsession: Using primitive data types instead of creating small classes for them.
  • Shotgun Surgery: A single change requires making many small changes in different classes.

Refactoring and TDD

Refactoring and Test-Driven Development (TDD) are closely related. The "Refactor" step in the Red-Green-Refactor cycle of TDD is where you clean up the code you wrote to make the test pass. A comprehensive test suite is a prerequisite for safe refactoring, as it provides a safety net to ensure that you don't break existing functionality.

Rust Example: Replace Conditional with Polymorphism

Before Refactoring:

enum Shape {
    Circle(f64),
    Square(f64),
}

fn area(shape: &Shape) -> f64 {
    match shape {
        Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
        Shape::Square(side) => side * side,
    }
}

After Refactoring:

trait Shape {
    fn area(&self) -> f64;
}

struct Circle {
    radius: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

struct Square {
    side: f64,
}

impl Shape for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

This refactoring makes the code more extensible. To add a new shape, you just need to create a new struct and implement the Shape trait, without modifying the existing code.

Conclusion

Refactoring is an essential practice for maintaining the health and longevity of a software system. It's not about adding new features, but about continuously improving the internal quality of the code. By embracing refactoring as a daily discipline, developers can create code that is easier to understand, cheaper to modify, and more resilient to change.