Logo

Anti-Patterns: What NOT to Do

5 min read
Lesson slides
1 / 15

Anti-Patterns: What NOT to Do

Common Traps When Applying Design Patterns

Anti-patterns are common responses to recurring problems that are usually ineffective and counterproductive. Understanding anti-patterns is as important as knowing design patterns.

Design Pattern Anti-Patterns

graph TD
    A[Anti-Patterns] --> B[Misuse of Patterns]
    A --> C[Architectural Anti-Patterns]
    A --> D[Code-Level Anti-Patterns]
    
    B --> B1[Golden Hammer]
    B --> B2[Pattern Obsession]
    B --> B3[Copy-Paste Programming]
    
    C --> C1[Big Ball of Mud]
    C --> C2[Spaghetti Code]
    C --> C3[God Object]
    
    D --> D1[Magic Numbers]
    D --> D2[Dead Code]
    D --> D3[Duplicate Code]

1. Pattern-Specific Anti-Patterns

Singleton Anti-Patterns

# ❌ ANTI-PATTERN: Singleton Abuse
class ConfigManager:  # Everything becomes singleton
    _instance = None
    # ... singleton implementation
 
class DatabaseManager:  # Another singleton
    _instance = None
    # ... singleton implementation
 
class LogManager:  # Yet another singleton
    _instance = None
    # ... singleton implementation
 
# Problems: Global state, testing difficulties, tight coupling
# ✅ BETTER APPROACH: Dependency Injection
class OrderService:
    def __init__(self, config, db, logger):
        self.config = config
        self.db = db  
        self.logger = logger
    
    def process_order(self, order):
        # Use injected dependencies
        pass

Factory Anti-Patterns

# ❌ ANTI-PATTERN: God Factory
class EverythingFactory:
    def create_object(self, type, subtype, variant, options):
        if type == "user":
            if subtype == "admin":
                if variant == "super":
                    # 50 lines of creation logic
                elif variant == "regular":
                    # 30 lines of creation logic
            elif subtype == "regular":
                # ... more nested conditions
        elif type == "product":
            # ... another 100 lines
        # This factory does too much!
# ✅ BETTER APPROACH: Specialized Factories
class UserFactory:
    def create_admin(self, level): pass
    def create_regular_user(self): pass
 
class ProductFactory:
    def create_physical_product(self): pass
    def create_digital_product(self): pass

2. Architectural Anti-Patterns

The God Object

classDiagram
    class GodObject {
        +processUser()
        +handlePayment()
        +sendEmail()
        +validateData()
        +generateReport()
        +manageInventory()
        +calculateTax()
        +logActivity()
        +... 50 more methods
    }
    
    note for GodObject "Violates Single Responsibility Principle"

Big Ball of Mud

The "Big Ball of Mud" anti-pattern refers to a software architecture or system design characterized by a lack of structure, organization, and clear separation of concerns. In a Big Ball of Mud architecture, the codebase typically evolves over time without a coherent architectural vision or efficient design decisions. As a result, the system becomes increasingly complex, tangled, difficult to understand, maintain, and extend.

graph LR
    A[Module A] -.-> B[Module B]
    B -.-> C[Module C]
    C -.-> A
    A -.-> D[Module D]
    D -.-> E[Module E]
    E -.-> B
    C -.-> F[Module F]
    F -.-> A
    E -.-> F
    
    style A fill:#ff9999
    style B fill:#ff9999
    style C fill:#ff9999
    style D fill:#ff9999
    style E fill:#ff9999
    style F fill:#ff9999

3. Common Design Anti-Patterns

Magic Numbers and Strings

# ❌ ANTI-PATTERN: Magic Numbers
def calculate_discount(price):
    if price > 1000:
        return price * 0.15  # What does 0.15 mean?
    elif price > 500:
        return price * 0.10  # What does 0.10 mean?
    return 0
 
def get_user_type(code):
    if code == 1:  # What does 1 mean?
        return "admin"
    elif code == 2:  # What does 2 mean?
        return "user"
# ✅ BETTER APPROACH: Named Constants
class DiscountRates:
    PREMIUM_THRESHOLD = 1000
    PREMIUM_RATE = 0.15
    STANDARD_THRESHOLD = 500
    STANDARD_RATE = 0.10
 
class UserTypes:
    ADMIN = 1
    REGULAR_USER = 2
 
def calculate_discount(price):
    if price > DiscountRates.PREMIUM_THRESHOLD:
        return price * DiscountRates.PREMIUM_RATE
    elif price > DiscountRates.STANDARD_THRESHOLD:
        return price * DiscountRates.STANDARD_RATE
    return 0

Copy-Paste Programming

# ❌ ANTI-PATTERN: Duplicate Code
def process_order_email(order):
    subject = f"Order {order.id} Confirmation"
    body = f"Dear {order.customer.name}, your order has been processed"
    send_email(order.customer.email, subject, body)
 
def process_shipment_email(order):
    subject = f"Order {order.id} Shipped"  # Similar code
    body = f"Dear {order.customer.name}, your order has been shipped"  # Similar code
    send_email(order.customer.email, subject, body)  # Duplicate
 
def process_delivery_email(order):
    subject = f"Order {order.id} Delivered"  # Similar code again
    body = f"Dear {order.customer.name}, your order has been delivered"  # Similar code again
    send_email(order.customer.email, subject, body)  # More duplication
# ✅ BETTER APPROACH: Template Method Pattern
class EmailNotification:
    def send_order_notification(self, order, event_type):
        subject = self._create_subject(order, event_type)
        body = self._create_body(order, event_type)
        send_email(order.customer.email, subject, body)
    
    def _create_subject(self, order, event_type):
        return f"Order {order.id} {event_type.title()}"
    
    def _create_body(self, order, event_type):
        messages = {
            'confirmation': 'has been processed',
            'shipped': 'has been shipped', 
            'delivered': 'has been delivered'
        }
        return f"Dear {order.customer.name}, your order {messages[event_type]}"

4. Anti-Pattern Detection Checklist

flowchart TD
    A[Code Review] --> B{Class > 500 lines?}
    B -->|Yes| C[Possible God Object]
    B -->|No| D{Method > 50 lines?}
    D -->|Yes| E[Extract Methods]
    D -->|No| F{Duplicate Code?}
    F -->|Yes| G[Apply DRY Principle]
    F -->|No| H{Magic Numbers?}
    H -->|Yes| I[Extract Constants]
    H -->|No| J{Tight Coupling?}
    J -->|Yes| K[Apply SOLID Principles]
    J -->|No| L[Code Looks Good!]

5. Refactoring Anti-Patterns

Before: Anti-Pattern Example

class OrderProcessor:  # God Object Anti-Pattern
    def process_order(self, order_data):
        # Validation (should be separate)
        if not order_data.get('email'):
            raise ValueError("Email required")
        
        # Payment processing (should be separate)
        if order_data['payment_method'] == 'credit_card':
            # 20 lines of credit card logic
            pass
        elif order_data['payment_method'] == 'paypal':
            # 15 lines of PayPal logic
            pass
        
        # Inventory management (should be separate)
        for item in order_data['items']:
            # 10 lines of inventory logic
            pass
        
        # Email notification (should be separate)
        # 15 lines of email logic
        
        # Logging (should be separate)
        # 5 lines of logging logic

After: Proper Pattern Application

class OrderProcessor:
    def __init__(self, validator, payment_processor, inventory, notifier, logger):
        self.validator = validator
        self.payment_processor = payment_processor
        self.inventory = inventory
        self.notifier = notifier
        self.logger = logger
    
    def process_order(self, order_data):
        self.validator.validate(order_data)
        payment_result = self.payment_processor.process(order_data)
        self.inventory.reserve_items(order_data['items'])
        self.notifier.send_confirmation(order_data)
        self.logger.log_order_processed(order_data)
        return payment_result

Anti-Pattern Prevention Guidelines

  1. Single Responsibility: Each class should have one reason to change
  2. Avoid Global State: Minimize singletons and global variables
  3. Favor Composition: Use dependency injection over inheritance
  4. Keep It Simple: Don't over-engineer solutions
  5. Regular Refactoring: Continuously improve code structure
  6. Code Reviews: Have others review your pattern usage
  7. Test-Driven Development: Write tests to catch design issues early