[TIPS] Refactoring - Clean Code - Tip 3: Break Down Large Functions

By JoeVu, at: 09:45 Ngày 08 tháng 7 năm 2024

Thời gian đọc ước tính: __READING_TIME__ minutes

[TIPS] Refactoring - Clean Code - Tip 3: Break Down Large Functions
[TIPS] Refactoring - Clean Code - Tip 3: Break Down Large Functions

Refactoring Tip 3: Break Down Large Functions

  • Junior Developer: Often writes large, monolithic functions.
     
  • Senior Developer: Breaks down large functions into smaller, single-responsibility functions.

Breaking down large functions into smaller, more focused ones enhances code readability, maintainability, and testability. Here's an example to illustrate the difference between how a junior and a senior developer might approach this principle:

 

Example 1: Refactoring a User Registration Process


Junior Developer's Approach:

A junior developer might write a large function that handles the entire user registration process.

def register_user(user_data):
    # Validate user data
    if 'username' not in user_data or 'password' not in user_data:
        return 'Invalid data'
    
    # Hash the password
    import hashlib
    hashed_password = hashlib.sha256(user_data['password'].encode()).hexdigest()
    
    # Save user to database
    import sqlite3
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS users
                      (username TEXT, password TEXT)''')
    cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)",
                   (user_data['username'], hashed_password))
    conn.commit()
    conn.close()
    
    # Send welcome email
    import smtplib
    from email.mime.text import MIMEText
    msg = MIMEText(f"Welcome {user_data['username']}!")
    msg['Subject'] = 'Welcome'
    msg['From'] = '[email protected]'
    msg['To'] = user_data['email']
    s = smtplib.SMTP('localhost')
    s.sendmail('[email protected]', [user_data['email']], msg.as_string())
    s.quit()
    
    return 'User registered successfully'

# Usage
result = register_user({
    'username': 'john_doe',
    'password': 'password123',
    'email': '[email protected]'
})

 

Senior Developer's Approach:

A senior developer would break down the large function into smaller, single-responsibility functions.

import hashlib
import sqlite3
import smtplib
from email.mime.text import MIMEText


def validate_user_data(user_data):
    if 'username' not in user_data or 'password' not in user_data or 'email' not in user_data:
        raise ValueError('Invalid data')


def hash_password(password):
    return hashlib.sha256(password.encode()).hexdigest()


def save_user_to_db(username, hashed_password):
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS users
                      (username TEXT, password TEXT)''')
    cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)",
                   (username, hashed_password))
    conn.commit()
    conn.close()


def send_welcome_email(username, email):
    msg = MIMEText(f"Welcome {username}!")
    msg['Subject'] = 'Welcome'
    msg['From'] = '[email protected]'
    msg['To'] = email
    s = smtplib.SMTP('localhost')
    s.sendmail('[email protected]', [email], msg.as_string())
    s.quit()


def register_user(user_data):
    validate_user_data(user_data)
    hashed_password = hash_password(user_data['password'])
    save_user_to_db(user_data['username'], hashed_password)
    send_welcome_email(user_data['username'], user_data['email'])
    return 'User registered successfully'


# Usage
result = register_user({
    'username': 'john_doe',
    'password': 'password123',
    'email': '[email protected]'
})

 

Example 2: Refactoring an Order Processing System


Junior Developer's Approach:

A junior developer might write a large function that handles the entire order processing workflow.

def process_order(order):
    # Validate order
    if 'items' not in order or 'customer' not in order:
        return 'Invalid order'
    
    # Calculate total
    total = 0
    for item in order['items']:
        total += item['price'] * item['quantity']
    
    # Process payment
    payment_success = True  # Simulate payment processing
    if not payment_success:
        return 'Payment failed'
    
    # Generate invoice
    invoice = {
        'customer': order['customer'],
        'items': order['items'],
        'total': total
    }
    
    # Send confirmation email
    import smtplib
    from email.mime.text import MIMEText
    msg = MIMEText(f"Thank you for your order, {order['customer']['name']}! Your total is ${total}.")
    msg['Subject'] = 'Order Confirmation'
    msg['From'] = '[email protected]'
    msg['To'] = order['customer']['email']
    s = smtplib.SMTP('localhost')
    s.sendmail('[email protected]', [order['customer']['email']], msg.as_string())
    s.quit()
    
    return 'Order processed successfully'


# Usage
order = {
    'customer': {'name': 'John Doe', 'email': '[email protected]'},
    'items': [{'name': 'Item A', 'price': 10, 'quantity': 2}, {'name': 'Item B', 'price': 20, 'quantity': 1}]
}
result = process_order(order)

 

Senior Developer's Approach:

A senior developer would break down the large function into smaller, single-responsibility functions.

import smtplib
from email.mime.text import MIMEText

def validate_order(order):
    if 'items' not in order or 'customer' not in order:
        raise ValueError('Invalid order')

def calculate_total(items):
    return sum(item['price'] * item['quantity'] for item in items)

def process_payment(order_total):
    payment_success = True  # Simulate payment processing
    if not payment_success:
        raise RuntimeError('Payment failed')

def generate_invoice(customer, items, total):
    return {
        'customer': customer,
        'items': items,
        'total': total
    }

def send_confirmation_email(customer, total):
    msg = MIMEText(f"Thank you for your order, {customer['name']}! Your total is ${total}.")
    msg['Subject'] = 'Order Confirmation'
    msg['From'] = '[email protected]'
    msg['To'] = customer['email']
    s = smtplib.SMTP('localhost')
    s.sendmail('[email protected]', [customer['email']], msg.as_string())
    s.quit()

def process_order(order):
    validate_order(order)
    total = calculate_total(order['items'])
    process_payment(total)
    invoice = generate_invoice(order['customer'], order['items'], total)
    send_confirmation_email(order['customer'], total)
    return 'Order processed successfully'

# Usage
order = {
    'customer': {'name': 'John Doe', 'email': '[email protected]'},
    'items': [{'name': 'Item A', 'price': 10, 'quantity': 2}, {'name': 'Item B', 'price': 20, 'quantity': 1}]
}
result = process_order(order)

 

Example 3: Refactoring API Integration


Junior Developer's Approach:

A junior developer might write a large function that handles multiple API interactions and processing steps.

import requests

def fetch_and_process(api_url):
    response = requests.get(api_url + '/data')
    data = response.json()

    filtered_data = []
    for item in data:
        if some_condition(item):
            filtered_data.append(item)

    processed_data = []
    for item in filtered_data:
        processed_data.append(some_processing(item))

    return processed_data

# Usage
result = fetch_and_process('https://api.example.com')

 

Senior Developer's Approach:

A senior developer would break down the large function into smaller, single-responsibility functions.

import requests


class DataFetcherProcessor:
    def __init__(self, base_url):
        self.base_url = base_url

    def fetch_data(self, endpoint):
        response = requests.get(f"{self.base_url}/{endpoint}")
        response.raise_for_status()
        return response.json()

    def filter_data(self, data, condition):
        return [item for item in data if condition(item)]

    def process_item(self, item):
        return self.some_processing(item)

    def fetch_and_process(self, endpoint, condition):
        data = self.fetch_data(endpoint)
        filtered_data = self.filter_data(data, condition)
        return [self.process_item(item) for item in filtered_data]

    def some_processing(self, item):
        # Implement the processing logic here
        pass


# Usage
def some_condition(item):
    # Define your condition here
    return True


fetcher_processor = DataFetcherProcessor('https://api.example.com')
result = fetcher_processor.fetch_and_process('data', some_condition)

 

 

These examples demonstrate how a senior developer's approach to breaking down large functions can lead to code that is more modular, maintainable, and easier to understand. By adhering to the single-responsibility principle, each function becomes more focused and easier to test, which ultimately improves the overall quality of the codebase.

Tag list:
- Tips
- Tips and Tricks
- Refactor Code
- clean code
- python tricks
- Refactor Tips
- Python tips
- Break Down Large Functions
- refactoring

Theo dõi

Theo dõi bản tin của chúng tôi và không bao giờ bỏ lỡ những tin tức mới nhất.