Refactoring is the process of restructuring existing computer code without changing its external behavior. It aims to improve the nonfunctional attributes of the software, making it easier to understand, more maintainable, and more efficient. In this section, we will cover the key concepts, techniques, and best practices for refactoring Ruby code.

Key Concepts

  1. Code Smells: Indicators that there might be a problem in the code.
  2. Refactoring Techniques: Specific methods used to improve the code.
  3. Automated Tests: Ensuring that refactoring does not break existing functionality.

Code Smells

Code smells are patterns in the code that may indicate deeper problems. Here are some common code smells:

  • Duplicated Code: Same code structure repeated in multiple places.
  • Long Method: Methods that are too long and do too much.
  • Large Class: Classes that have too many responsibilities.
  • Feature Envy: A method that seems more interested in a class other than the one it is in.
  • Data Clumps: Groups of data that are often passed together.

Refactoring Techniques

  1. Extract Method

Problem: A method is too long or does too many things.

Solution: Break the method into smaller, more focused methods.

# Before Refactoring
def print_owing
  outstanding = 0.0
  puts "*"
  puts "* Customer Owes ***"
  puts "*"
  @orders.each do |order|
    outstanding += order.amount
  end
  puts "name: #{@name}"
  puts "amount: #{outstanding}"
end

# After Refactoring
def print_owing
  print_banner
  outstanding = calculate_outstanding
  print_details(outstanding)
end

def print_banner
  puts "*"
  puts "* Customer Owes ***"
  puts "*"
end

def calculate_outstanding
  @orders.reduce(0.0) { |sum, order| sum + order.amount }
end

def print_details(outstanding)
  puts "name: #{@name}"
  puts "amount: #{outstanding}"
end

  1. Inline Method

Problem: A method is not doing enough to justify its existence.

Solution: Replace the method call with the method's content.

# Before Refactoring
def get_rating
  more_than_five_late_deliveries ? 2 : 1
end

def more_than_five_late_deliveries
  @number_of_late_deliveries > 5
end

# After Refactoring
def get_rating
  @number_of_late_deliveries > 5 ? 2 : 1
end

  1. Rename Method

Problem: A method name does not clearly describe what the method does.

Solution: Rename the method to something more descriptive.

# Before Refactoring
def get_annual_income
  # method implementation
end

# After Refactoring
def calculate_annual_income
  # method implementation
end

  1. Replace Temp with Query

Problem: A temporary variable is holding the result of an expression.

Solution: Replace the variable with a method call.

# Before Refactoring
base_price = quantity * item_price
if base_price > 1000
  # do something
end

# After Refactoring
if base_price > 1000
  # do something
end

def base_price
  quantity * item_price
end

Automated Tests

Before refactoring, it is crucial to have a comprehensive suite of automated tests. These tests ensure that the refactoring process does not introduce new bugs. Here are some steps to follow:

  1. Write Tests: Ensure you have tests that cover the existing functionality.
  2. Run Tests: Run all tests to ensure they pass before refactoring.
  3. Refactor: Apply the refactoring techniques.
  4. Run Tests Again: Run all tests again to ensure no functionality is broken.

Practical Exercise

Exercise: Refactor the Following Code

Refactor the following code to improve its readability and maintainability.

class Order
  def initialize(customer)
    @customer = customer
  end

  def print_order
    puts "Order for #{@customer.name}"
    puts "Items:"
    @customer.orders.each do |order|
      puts "#{order.item_name}: #{order.price}"
    end
    total = @customer.orders.reduce(0) { |sum, order| sum + order.price }
    puts "Total: #{total}"
  end
end

Solution

class Order
  def initialize(customer)
    @customer = customer
  end

  def print_order
    print_header
    print_items
    print_total
  end

  private

  def print_header
    puts "Order for #{@customer.name}"
    puts "Items:"
  end

  def print_items
    @customer.orders.each do |order|
      puts "#{order.item_name}: #{order.price}"
    end
  end

  def print_total
    total = calculate_total
    puts "Total: #{total}"
  end

  def calculate_total
    @customer.orders.reduce(0) { |sum, order| sum + order.price }
  end
end

Conclusion

Refactoring is an essential skill for maintaining and improving the quality of your code. By identifying code smells and applying appropriate refactoring techniques, you can make your code more readable, maintainable, and efficient. Always ensure you have a robust suite of automated tests to safeguard against introducing new bugs during the refactoring process.

© Copyright 2024. All rights reserved