Lesson 1
Eliminating Code Duplication and Refactoring Magic Numbers in Ruby
Introduction and Context Setting

Welcome to our session on eliminating duplicated code and improving maintainability by extracting methods and refactoring magic numbers. Writing clean and maintainable code is a key element in modern software development, with refactoring playing a vital role in achieving this. This lesson is dedicated to identifying duplicate code sections and restructuring them to enhance readability and maintainability.

In this course, we use Ruby, a dynamic, reflective, object-oriented, and general-purpose programming language, along with RSpec, a Behavior Driven Development (BDD) framework for Ruby. These tools aid us in writing reliable tests and handling code changes smoothly.

This lesson continues from an existing ShoppingCart example to focus on the 'Refactor' aspect of the 'Red, Green, Refactor' cycle. Let's dive into identifying and restructuring duplicated code to improve our codebase.

What is a Code Smell and How Do You Eliminate Them?

Before focusing on specific "smells," we should first understand what "code smells" signify. Code smells suggest potential issues in the code, such as poorly structured code or potential defects, even though they aren't bugs themselves. Common examples include duplicate code, long methods, and magic numbers. These issues often hinder code readability and maintainability, leading to significant problems over time.

Refactoring patterns offer structured techniques to tackle these code smells and improve the overall quality of the codebase while preventing behavioral changes. By using these patterns, we can systematically transform problematic sections into cleaner, more efficient code. In this lesson, we'll combat duplications by applying refactoring patterns, like extracting methods and refactoring magic numbers, to enhance our application code's clarity and adaptability. We can rely on our tests to ensure that we are not breaking any existing functionality!

Understanding Code Duplication

When similar or identical code segments are replicated across a codebase, it leads to a "Code Duplication" issue or smell. This can result in maintenance difficulties and promote bugs. Consider, for instance, when constant values are spread across a codebase without a clear label. This complicates updates or adjustments, as changes need to be applied in multiple places, increasing the risk of errors.

Maintaining the DRY (Don't Repeat Yourself) principle ensures that every piece of knowledge has a singular, unambiguous representation within the system. This enhances maintainability and understanding while reducing debugging efforts. Remember, refactoring to eliminate code duplication will be part of our work routine in TDD, providing minimal disturbance to existing functionalities.

Example: Extracting Methods

We'll look at how to extract common logic blocks as stand-alone methods using our ShoppingCart class. This example assumes we have repetitive logic that calculates the total cost for each item while considering potential discounts. We're starting with a set of passing tests that clearly demonstrate duplication in their implementation.

Refactor - Optimize the Implementation

During refactoring, we fit calculate_item_cost into the ShoppingCart class so that all methods needing this logic can easily employ it. Ensuring that functionality remains the same while the code quality is enhanced is the essence of TDD.

Ruby
1class ShoppingCart 2 def initialize 3 @items = [] # Initialize an empty list to hold items 4 end 5 6 def add_item(name, price, quantity) 7 @items << { name: name, price: price, quantity: quantity } # Add each item as a hash to the items list 8 end 9 10 def calculate_total 11 total = 0 # Initialize a variable to keep the total cost 12 13 @items.each do |item| 14 item_total = item[:price] * item[:quantity] # Calculate total price for each item (price x quantity) 15 16 total += item_total # Add item's total price to the total cost 17 end 18 19 total # Return the computed total cost for all items 20 end 21 22 def calculate_total_with_student_discount 23 total = 0 # Initialize a variable to keep the total cost 24 25 @items.each do |item| 26 item_total = item[:price] * item[:quantity] # Calculate total price for each item (price x quantity) 27 28 total += item_total # Add item's total price to the total cost 29 end 30 31 # Apply student discount 32 total * 0.85 # Return the discounted total cost applying a 15% discount 33 end 34end

The repetitive logic can be noticed within calculate_total and calculate_total_with_student_discount methods.

Refactor: Extract Method

We can extract a method called calculate_subtotal that can be used by calculate_total and calculate_total_with_student_discount to get rid of the redundancy!

Ruby
1class ShoppingCart 2 def initialize 3 @items = [] # Initialize an empty list to hold items 4 end 5 6 def add_item(name, price, quantity) 7 @items << { name: name, price: price, quantity: quantity } # Add each item as a hash to the items list 8 end 9 10 def calculate_subtotal 11 subtotal = 0 # Initialize a variable to keep the subtotal cost 12 13 @items.each do |item| 14 subtotal += item[:price] * item[:quantity] # Calculate and add the cost of each item to the subtotal 15 end 16 17 subtotal # Return the computed subtotal cost for all items 18 end 19 20 def calculate_total 21 calculate_subtotal # Use the extracted method to calculate and return the total 22 end 23 24 def calculate_total_with_student_discount 25 subtotal = calculate_subtotal # Calculate subtotal using the extracted method 26 27 # Apply student discount 28 subtotal * 0.85 # Return the discounted total cost applying a 15% discount 29 end 30end
Example: Refactoring Magic Numbers

Now let's see "Magic Numbers." These are exact numbers that make an appearance in your code. Magic Numbers can cause maintenance headaches when these values need to be updated in the future, especially if they appear at different locations.

Here is an example:

Ruby
1def calculate_item_cost(item) 2 item_total = item[:price] * item[:quantity] # Calculate the total cost of the item (price x quantity) 3 4 if item[:quantity] >= 5 # Check if the quantity is greater than or equal to the threshold for bulk discount 5 item_total *= 0.9 # Apply a 10% discount 6 end 7 8 item_total # Return the total item cost after any discounts 9end
Refactor: Extract Magic Numbers

The solution to magic numbers is simple: make them well-defined constants that can be reused throughout the codebase.

Ruby
1BULK_DISCOUNT_THRESHOLD = 5 # Constant representing the minimum quantity to qualify for a bulk discount 2BULK_DISCOUNT_RATE = 0.9 # Constant representing a 10% discount rate 3 4def calculate_item_cost(item) 5 item_total = item[:price] * item[:quantity] # Calculate the total cost of the item (price x quantity) 6 7 if item[:quantity] >= BULK_DISCOUNT_THRESHOLD # Use threshold constant to check eligibility for bulk discount 8 item_total *= BULK_DISCOUNT_RATE # Use discount rate constant to apply a 10% discount 9 end 10 11 item_total # Return the total item cost after any discounts 12end
Review, Summary, and Preparation for Practice

In this lesson, we explored the importance of refactoring in eliminating code duplication and magic numbers. By sticking to the TDD cycle — Red (write a failing test), Green (make it pass with bare minimum code), and Refactor (clean the code without changing behavior) — we ensure our code is both functional and maintainable.

Now, as you get ready for the upcoming practice exercises, concentrate on leveraging these skills to improve your ability to manage intricate codebases. Apply the concepts of DRY and TDD consistently to keep your code robust and adaptable. Keep practicing and mastering these principles, and you'll soon find yourself capable of developing highly efficient and scalable applications.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.