Lesson 1
Applying the DRY Principle in Python
Introduction

Welcome to the very first lesson of the "Applying Clean Code Principles" course! In this lesson, we will focus on a fundamental concept in clean coding: the DRY ("Don't Repeat Yourself") principle. Understanding DRY is crucial for writing efficient, maintainable, and clean code. This principle is important not just for coding interviews but also in everyday software development. Today, we will dive deep into issues caused by repetitive code and explore strategies to combat redundancy. 🚀

Understanding the Problem

Repetitive functionality in code can introduce several issues that affect the efficiency and maintainability of your software:

  • Code Bloat: Repeating similar code across different parts of your application unnecessarily increases the size of the codebase. This makes the code harder to navigate and increases the chances of introducing errors.

  • Risk of Inconsistencies: When similar pieces of logic are scattered across different areas, they can easily become out of sync during updates or bug fixes. This can result in logic discrepancies and potentially introduce new problems.

  • Maintenance Challenges: Updating code often requires modifications in multiple places, leading to increased work and a higher likelihood of errors. Redundant code makes it difficult for developers to ensure all necessary changes have been made consistently.

DRY Strategies

To adhere to the DRY principle and avoid repeating yourself, several strategies can be employed:

  • Extracting Function: Move repeated logic into a dedicated function that can be reused wherever needed. This promotes reuse and simplifies updates.

  • Extracting Variable: Consolidate repeated expressions or values into variables. This centralizes change, reducing the potential for errors.

  • Replace Temp with Function: Use a function to compute values on demand rather than storing them in temporary variables, aiding in readability and reducing redundancy.

Extracting Function

Consider the following problematic code snippet where repetitive logic is used for calculating the total price based on different shipping methods:

Python
1def calculate_click_and_collect_total(order): 2 items_total = 0 3 for item in order.get_items(): 4 items_total += item.get_price() * item.get_quantity() 5 shipping_cost = 0 if items_total > 100 else 5 6 return items_total + shipping_cost + order.get_tax() 7 8def calculate_post_shipment_total(order, is_express): 9 items_total = 0 10 for item in order.get_items(): 11 items_total += item.get_price() * item.get_quantity() 12 shipping_cost = items_total * 0.1 if is_express else items_total * 0.05 13 return items_total + shipping_cost + order.get_tax()

Both functions contain duplicated logic for calculating the total price of items, making them error-prone and hard to maintain. Now, let's refactor this code.

Extracting Function: Refactored

By consolidating the shared logic into a separate function, we can eliminate redundancy and streamline updates:

Python
1def calculate_click_and_collect_total(order): 2 items_total = calculate_items_total(order) 3 shipping_cost = 0 if items_total > 100 else 5 4 return items_total + shipping_cost + order.get_tax() 5 6def calculate_post_shipment_total(order, is_express): 7 items_total = calculate_items_total(order) 8 shipping_cost = items_total * 0.1 if is_express else items_total * 0.05 9 return items_total + shipping_cost + order.get_tax() 10 11def calculate_items_total(order): 12 items_total = 0 13 for item in order.get_items(): 14 items_total += item.get_price() * item.get_quantity() 15 return items_total

By extracting the calculate_items_total function, we centralize the logic of item total calculation, leading to cleaner, more maintainable code.

Extracting Variable

The Extracting Variable technique involves identifying expressions or values within your code that are repeated, complex, or unclear, and consolidating them into named variables. Let's look at another example dealing with repeated calculations for discount rates:

Python
1def apply_discount(price, customer): 2 loyalty_discount_rate = customer.get_loyalty_level() * 0.02 3 price *= (1 - loyalty_discount_rate) 4 # Additional discounts 5 seasonal_discount_rate = 0.10 6 price *= (1 - seasonal_discount_rate) 7 return price

Here, the discount rates are scattered throughout the code, which complicates management and updates. Without extracting the discount rates into named variables, the code lacks a clear structure, making it harder to decipher the purpose and role of each calculation. This approach hinders the code's adaptability, as changes to the discount logic necessitate locating every instance where these literal values are used, complicating management.

Extracting Variable: Refactored

We can simplify this by extracting the discount rates into variables:

Python
1def apply_discount(price, customer): 2 loyalty_discount_rate = customer.get_loyalty_level() * 0.02 3 seasonal_discount_rate = 0.10 4 5 loyalty_adjusted_price = price * (1 - loyalty_discount_rate) 6 final_price = loyalty_adjusted_price * (1 - seasonal_discount_rate) 7 8 return final_price

In the refactored version, we improve clarity by explicitly naming the calculation steps, such as loyalty_adjusted_price and final_price, which clearly delineate each phase of the discount application process. This organization makes the sequence of operations more transparent and ensures that any future changes to the discount logic can be made with confidence and simplicity. 🎉

Replace Temp with Function

Our final example involves temporary variables that lead to repetition:

Python
1def is_eligible_for_discount(customer): 2 new_customer = customer.get_sign_up_date() > datetime.now() - timedelta(days=90) 3 return new_customer and len(customer.get_purchase_history()) > 5 4 5def is_eligible_for_loyalty_program(customer): 6 new_customer = customer.get_sign_up_date() > datetime.now() - timedelta(days=90) 7 return new_customer or customer.get_loyalty_level() > 3

The variable new_customer is used in multiple places, causing duplicated logic.

Replace Temp with Function: Refactored

Let's refactor by extracting the logic into a function, reducing duplication and enhancing modularity:

Python
1def is_eligible_for_discount(customer): 2 return is_new_customer(customer) and len(customer.get_purchase_history()) > 5 3 4def is_eligible_for_loyalty_program(customer): 5 return is_new_customer(customer) or customer.get_loyalty_level() > 3 6 7def is_new_customer(customer): 8 return customer.get_sign_up_date() > datetime.now() - timedelta(days=90)

By creating the is_new_customer function, we've simplified the code and made it more maintainable. 🚀

Summary and Preparation for Practice

In this lesson, you learned about the DRY principle and strategies like Extracting Function, Extracting Variable, and Replace Temp with Function to eliminate code redundancy. These strategies help to create code that is easier to maintain, enhance, and understand. Next, you'll have the opportunity to apply these concepts in practical exercises, strengthening your ability to refactor code and uphold clean coding standards. Happy coding! 😊

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