Design Pattern 27: Visitor Pattern - Complete Guide with Real-World IoT Examples

πŸ“ Download the complete Design Pattern series code from our design_pattern repository.


🎯 What is the Visitor Pattern?

The Visitor Pattern is a behavioral design pattern that lets you add new operations to existing object structures without modifying their classes. It separates algorithms from the objects on which they operate, making it easy to extend and maintain complex systems.

Key Benefits:

  • βœ… Open/Closed Principle - Add new operations without changing object structure
  • βœ… Centralized logic - Keep related operations together
  • βœ… Extensibility - Easily support new operations and object types
  • βœ… Maintainability - Clean separation of concerns
  • βœ… Scalability - Ideal for large, evolving systems

πŸš€ Real-World Problem: IoT App Integrating Multiple IPCam Brands

Suppose you are building an IoT app that needs to support multiple IPCam brands, each with different streaming and snapshot APIs:

System Requirements:

  • Support for multiple IPCam brands (e.g., HIKVISION, DAHUA)
  • Each brand provides different streaming and snapshot methods
  • App code should not depend on brand-specific details
  • Easy to add new brands in the future
  • Avoid modifying core IPCam structure (often vendor-provided)

Business Rules:

  • All IPCam operations (streaming, snapshot) must be extensible
  • New operations should not require changes to existing IPCam classes
  • Maintain clean, maintainable codebase

πŸ—οΈ Object-Oriented Analysis (OOA)

Let’s analyze the problem and identify the core components:

Identified Forces:

  1. Difficult to extend for new brands
  2. Violation of Open/Closed Principle (OCP)
  3. Inconsistent handling of brand-specific operations

πŸ’‘ Visitor Pattern Solution

After analyzing the forces, we can apply the Visitor Pattern to decouple operations from object structure:

Visitor Pattern Components:

  1. Visitor Interface - Defines operations for each object type
  2. Concrete Visitors - Implement specific operations (e.g., streaming, snapshot)
  3. Element Interface - Defines accept(visitor) method
  4. Concrete Elements - Implement accept and brand-specific logic

Benefits:

  • Add new operations easily (just add a new visitor)
  • Centralize operation logic
  • Keep object structure stable

πŸ› οΈ Implementation: IPCam Integration Example

1. Element Interface

interface IPCam {
    fun accept(visitor: IPCamVisitor)
}

2. Concrete Elements

class HikvisionIPCam : IPCam {
    override fun accept(visitor: IPCamVisitor) {
        visitor.visitHikvision(this)
    }
    fun getRTSPStream(): String = "rtsp://hikvision/stream"
    fun captureSnapshot(): String = "Hikvision Snapshot"
}

class DahuaIPCam : IPCam {
    override fun accept(visitor: IPCamVisitor) {
        visitor.visitDahua(this)
    }
    fun startSDKStream(): String = "Dahua SDK Stream"
    fun takeSDKSnapshot(): String = "Dahua Snapshot"
}

3. Visitor Interface

interface IPCamVisitor {
    fun visitHikvision(ipCam: HikvisionIPCam)
    fun visitDahua(ipCam: DahuaIPCam)
}

4. Concrete Visitors

class IPCamStreamingVisitor : IPCamVisitor {
    override fun visitHikvision(ipCam: HikvisionIPCam) {
        println("Streaming: ${ipCam.getRTSPStream()}")
    }
    override fun visitDahua(ipCam: DahuaIPCam) {
        println("Streaming: ${ipCam.startSDKStream()}")
    }
}

class IPCamSnapshotVisitor : IPCamVisitor {
    override fun visitHikvision(ipCam: HikvisionIPCam) {
        println("Snapshot: ${ipCam.captureSnapshot()}")
    }
    override fun visitDahua(ipCam: DahuaIPCam) {
        println("Snapshot: ${ipCam.takeSDKSnapshot()}")
    }
}

5. Client Code

fun main() {
    val ipCams: List<IPCam> = listOf(HikvisionIPCam(), DahuaIPCam())
    val streamingVisitor = IPCamStreamingVisitor()
    val snapshotVisitor = IPCamSnapshotVisitor()
    for (ipCam in ipCams) {
        ipCam.accept(streamingVisitor)
        ipCam.accept(snapshotVisitor)
    }
}

Expected Output:

Streaming: rtsp://hikvision/stream
Snapshot: Hikvision Snapshot
Streaming: Dahua SDK Stream
Snapshot: Dahua Snapshot

πŸ“Š Visitor Pattern vs Alternative Approaches

Approach Pros Cons
Visitor Pattern βœ… Add new operations easily
βœ… Centralized logic
❌ Must update visitor for new element types
❌ Double dispatch complexity
Strategy Pattern βœ… Runtime flexibility
βœ… No double dispatch
❌ No access to element internals
❌ Harder to add new operations
Direct Inheritance βœ… Simple for small systems ❌ Tight coupling
❌ Hard to extend

🎯 When to Use the Visitor Pattern

βœ… Perfect For:

  • Complex object structures (ASTs, document models)
  • IoT device integration (multi-brand support)
  • Compilers and interpreters (expression evaluation)
  • UI component trees (rendering, event handling)
  • Code analysis tools (linting, refactoring)

❌ Avoid When:

  • Element types change frequently (must update all visitors)
  • Simple, flat object structures
  • Performance-critical code (double dispatch overhead)

πŸ”§ Advanced Visitor Pattern Implementations

1. Adding New Operations

class IPCamFirmwareUpgradeVisitor : IPCamVisitor {
    override fun visitHikvision(ipCam: HikvisionIPCam) {
        println("Upgrading firmware for Hikvision")
    }
    override fun visitDahua(ipCam: DahuaIPCam) {
        println("Upgrading firmware for Dahua")
    }
}

2. Adding New IPCam Brands

class EzvizIPCam : IPCam {
    override fun accept(visitor: IPCamVisitor) {
        // Add visitEzviz to IPCamVisitor and all concrete visitors
        // visitor.visitEzviz(this)
    }
    fun getEzvizStream(): String = "Ezviz Stream"
    fun captureEzvizSnapshot(): String = "Ezviz Snapshot"
}

πŸ“ˆ Real-World Applications

1. IoT Device Management

  • Multi-brand camera integration
  • Unified device operations (streaming, snapshot, firmware upgrade)

2. Compilers and Interpreters

  • Abstract Syntax Tree (AST) traversal
  • Code generation, optimization, and analysis

3. UI Component Trees

  • Rendering, event handling, and layout

4. Code Analysis Tools

  • Linting, static analysis, refactoring

🚨 Common Pitfalls and Best Practices

1. Double Dispatch Complexity

  • Visitor pattern uses double dispatch; keep visitor interfaces up to date

2. Element Explosion

  • Too many element types can make visitor maintenance harder

3. Best Practices

  • Use clear naming for visitor methods (e.g., visitHikvision)
  • Document all supported element types in visitor interface
  • Use abstract base classes for shared logic


βœ… Conclusion

Through the Visitor Pattern, we successfully decoupled IPCam operations from their structure, enabling easy extension and centralized logic management.

Key Advantages:

  • 🎯 Open/Closed Principle - Add new operations without changing object structure
  • πŸ”§ Centralized logic - Keep related operations together
  • πŸ“ˆ Easy extension - Add new operations or brands with minimal changes
  • πŸ›‘οΈ Maintainability - Clean separation of concerns
  • ⚑ Scalability - Ideal for large, evolving systems

Design Principles Followed:

  • Single Responsibility Principle (SRP): Separate operation logic from object structure
  • Open-Closed Principle (OCP): Open for extension, closed for modification
  • Don’t Repeat Yourself (DRY): Centralize operation logic

Perfect For:

  • IoT device integration (multi-brand support)
  • AST traversal (compilers, interpreters)
  • UI frameworks (component trees)
  • Code analysis tools (linting, refactoring)

The Visitor Pattern provides an elegant solution for adding new operations to complex object structures while maintaining a clean, extensible codebase!


πŸ’‘ Pro Tip: Use the Visitor Pattern when you need to add many unrelated operations to object structures, but avoid it if your element types change frequently.

πŸ”” Stay Updated: Follow our Design Pattern series for more software architecture insights!




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • How to Use Multiple GitHub Accounts on One Computer: Complete SSH Setup Guide
  • Excalidraw AI: Create Professional Diagrams with Text Commands - Complete Guide
  • Complete macOS Development Environment Setup Guide for 2024
  • Design Pattern 28: Interpreter Pattern - Complete Guide with Examples
  • Design Pattern 26: Template Method Pattern - Complete Guide with Real-World Examples