Skip to content
IRC-Coding IRC-Coding
OOP Dispatch Dynamic Binding Single Dispatch Double Dispatch Visitor Pattern Multiple Dispatch vtable Polymorphism

OOP Dispatch: Dynamic Binding & Double Dispatch

Master OOP dispatch with dynamic binding, single/double dispatch, and the Visitor Pattern. Java examples included.

S

schutzgeist

2 min read

OOP Dispatch: Dynamic Binding, Single & Double Dispatch

Dispatch refers to the selection of the actual method to be executed during a method call. This selection can occur statically at compile time or dynamically at runtime and is the foundation for polymorphism.

What is Dispatch?

Dispatch is the mechanism that resolves a method call to a concrete implementation. The type of dispatch resolution influences the flexibility and performance of the software.

Types of Dispatch

  1. Static Dispatch: Decision at compile time
  2. Dynamic Dispatch: Decision at runtime
  3. Single Dispatch: Selection based on receiver object
  4. Double Dispatch: Selection based on two object types
  5. Multiple Dispatch: Selection based on multiple object types

Static vs Dynamic Dispatch

Static Dispatch (Overloading)

public class StaticDispatchDemo {
    
    // Overloaded methods - static dispatch
    public static class Printer {
        public void print(Object obj) {
            System.out.println("Printing Object: " + obj.toString());
        }
        
        public void print(String str) {
            System.out.println("Printing String: " + str);
        }
        
        public void print(Integer num) {
            System.out.println("Printing Integer: " + num);
        }
        
        public void print(String str, int count) {
            for (int i = 0; i < count; i++) {
                System.out.println("String " + (i + 1) + ": " + str);
            }
        }
    }
    
    public static void demonstrateStaticDispatch() {
        Printer printer = new Printer();
        
        // Direct calls - unambiguous selection
        printer.print("Hello");           // print(String)
        printer.print(42);               // print(Integer)
        printer.print("Test", 3);         // print(String, int)
        
        // Important case: Static types decide!
        Object obj1 = "Hello";
        Object obj2 = 42;
        
        printer.print(obj1);  // print(Object) - Compiler knows only Object type!
        printer.print(obj2);  // print(Object) - Compiler knows only Object type!
        
        // Cast to select the right overload
        printer.print((String) obj1);   // print(String)
        // printer.print((Integer) obj2); // ClassCastException at runtime!
    }
}

Dynamic Dispatch (Overriding)

public class DynamicDispatchDemo {
    
    // Base class with virtual methods
    public static abstract class Animal {
        protected String name;
        
        public Animal(String name) {
            this.name = name;
        }
        
        // Virtual method - dynamically bound
        public String makeSound() {
            return name + " makes a sound";
        }
        
        // Another virtual method
        public String move() {
            return name + " moves";
        }
        
        // Final method - cannot be overridden
        public final String getName() {
            return name;
        }
        
        // Abstract method - must be overridden
        public abstract String getType();
    }
    
    // Concrete subclasses
    public static class Dog extends Animal {
        public Dog(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " barks: Woof!";
        }
        
        @Override
        public String move() {
            return name + " runs happily";
        }
        
        @Override
        public String getType() {
            return "Dog";
        }
        
        // Additional method
        public String wagTail() {
            return name + " wags tail excitedly";
        }
    }
    
    public static class Cat extends Animal {
        public Cat(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " meows: Meow!";
        }
        
        @Override
        public String move() {
            return name + " sneaks silently";
        }
        
        @Override
        public String getType() {
            return "Cat";
        }
        
        public String purr() {
            return name + " purrs contentedly";
        }
    }
    
    public static class Bird extends Animal {
        public Bird(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " chirps: Tweet!";
        }
        
        @Override
        public String move() {
            return name + " flies gracefully";
        }
        
        @Override
        public String getType() {
            return "Bird";
        }
        
        public String sing() {
            return name + " sings a beautiful song";
        }
    }
    
    // Demonstration of dynamic dispatch
    public static void demonstrateDynamicDispatch() {
        List<Animal> animals = new ArrayList<>();
        animals.add(new Dog("Buddy"));
        animals.add(new Cat("Whiskers"));
        animals.add(new Bird("Tweety"));
        
        System.out.println("=== Dynamic Dispatch ===");
        for (Animal animal : animals) {
            // Dynamic binding - runtime decides!
            System.out.println(animal.getType() + ": " + animal.makeSound());
            System.out.println(animal.getType() + ": " + animal.move());
            System.out.println();
        }
        
        // Polymorphic processing
        processAnimal(animals.get(0)); // Dog
        processAnimal(animals.get(1)); // Cat
        processAnimal(animals.get(2)); // Bird
    }
    
    // Polymorphic method
    public static void processAnimal(Animal animal) {
        System.out.println("Processing " + animal.getType());
        
        // Dynamic dispatch
        String sound = animal.makeSound();
        String movement = animal.move();
        
        System.out.println("  Sound: " + sound);
        System.out.println("  Movement: " + movement);
        
        // Type-specific operations (with instanceof)
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            System.out.println("  Special: " + dog.wagTail());
        } else if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            System.out.println("  Special: " + cat.purr());
        } else if (animal instanceof Bird) {
            Bird bird = (Bird) animal;
            System.out.println("  Special: " + bird.sing());
        }
    }
}

Single Dispatch

How Single Dispatch Works

public class SingleDispatchDemo {
    
    // Single Dispatch - only the receiver type decides
    public interface Shape {
        double area();
        double perimeter();
        String getType();
        void accept(ShapeVisitor visitor);
    }
    
    public static class Circle implements Shape {
        private double radius;
        
        public Circle(double radius) {
            this.radius = radius;
        }
        
        @Override
        public double area() {
            return Math.PI * radius * radius;
        }
        
        @Override
        public double perimeter() {
            return 2 * Math.PI * radius;
        }
        
        @Override
        public String getType() {
            return "Circle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitCircle(this); // Single Dispatch
        }
        
        public double getRadius() { return radius; }
    }
    
    public static class Rectangle implements Shape {
        private double width, height;
        
        public Rectangle(double width, double height) {
            this.width = width;
            this.height = height;
        }
        
        @Override
        public double area() {
            return width * height;
        }
        
        @Override
        public double perimeter() {
            return 2 * (width + height);
        }
        
        @Override
        public String getType() {
            return "Rectangle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitRectangle(this); // Single Dispatch
        }
        
        public double getWidth() { return width; }
        public double getHeight() { return height; }
    }
    
    public static class Triangle implements Shape {
        private double base, height;
        
        public Triangle(double base, double height) {
            this.base = base;
            this.height = height;
        }
        
        @Override
        public double area() {
            return 0.5 * base * height;
        }
        
        @Override
        public double perimeter() {
            // Simplified: equilateral triangle
            double side = base;
            return 3 * side;
        }
        
        @Override
        public String getType() {
            return "Triangle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitTriangle(this); // Single Dispatch
        }
        
        public double getBase() { return base; }
        public double getHeight() { return height; }
    }
    
    // Demonstration
    public static void demonstrateSingleDispatch() {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(2.0));
        shapes.add(new Rectangle(3.0, 4.0));
        shapes.add(new Triangle(5.0, 3.0));
        
        System.out.println("=== Single Dispatch ===");
        for (Shape shape : shapes) {
            System.out.println(shape.getType());
            System.out.println("  Area: " + String.format("%.2f", shape.area()));
            System.out.println("  Perimeter: " + String.format("%.2f", shape.perimeter()));
            System.out.println();
        }
    }
}

Double Dispatch with Visitor Pattern

Visitor Pattern for Double Dispatch

public class DoubleDispatchDemo {
    
    // Visitor Interface
    public interface ShapeVisitor {
        void visitCircle(Circle circle);
        void visitRectangle(Rectangle rectangle);
        void visitTriangle(Triangle triangle);
        double getResult();
    }
    
    // Konkreter Visitor für Flächenberechnung
    public static class AreaVisitor implements ShapeVisitor {
        private double totalArea = 0.0;
        
        @Override
        public void visitCircle(Circle circle) {
            double area = Math.PI * circle.getRadius() * circle.getRadius();
            totalArea += area;
            System.out.println("Circle area: " + String.format("%.2f", area));
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            double area = rectangle.getWidth() * rectangle.getHeight();
            totalArea += area;
            System.out.println("Rectangle area: " + String.format("%.2f", area));
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            double area = 0.5 * triangle.getBase() * triangle.getHeight();
            totalArea += area;
            System.out.println("Triangle area: " + String.format("%.2f", area));
        }
        
        @Override
        public double getResult() {
            return totalArea;
        }
    }
    
    // Visitor für Umfangberechnung
    public static class PerimeterVisitor implements ShapeVisitor {
        private double totalPerimeter = 0.0;
        
        @Override
        public void visitCircle(Circle circle) {
            double perimeter = 2 * Math.PI * circle.getRadius();
            totalPerimeter += perimeter;
            System.out.println("Circle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
            totalPerimeter += perimeter;
            System.out.println("Rectangle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            double perimeter = 3 * triangle.getBase(); // Vereinfacht
            totalPerimeter += perimeter;
            System.out.println("Triangle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public double getResult() {
            return totalPerimeter;
        }
    }
    
    // Visitor für XML-Export
    public static class XMLExportVisitor implements ShapeVisitor {
        private StringBuilder xml = new StringBuilder();
        
        @Override
        public void visitCircle(Circle circle) {
            xml.append("<shape type=\"circle\">\n");
            xml.append("  <radius>").append(circle.getRadius()).append("</radius>\n");
            xml.append("  <area>").append(String.format("%.2f", Math.PI * circle.getRadius() * circle.getRadius())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            xml.append("<shape type=\"rectangle\">\n");
            xml.append("  <width>").append(rectangle.getWidth()).append("</width>\n");
            xml.append("  <height>").append(rectangle.getHeight()).append("</height>\n");
            xml.append("  <area>").append(String.format("%.2f", rectangle.getWidth() * rectangle.getHeight())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            xml.append("<shape type=\"triangle\">\n");
            xml.append("  <base>").append(triangle.getBase()).append("</base>\n");
            xml.append("  <height>").append(triangle.getHeight()).append("</height>\n");
            xml.append("  <area>").append(String.format("%.2f", 0.5 * triangle.getBase() * triangle.getHeight())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public double getResult() {
            return 0; // Nicht relevant für diesen Visitor
        }
        
        public String getXML() {
            return "<shapes>\n" + xml.toString() + "</shapes>";
        }
    }
    
    // Demonstration von Double Dispatch
    public static void demonstrateDoubleDispatch() {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(2.0));
        shapes.add(new Rectangle(3.0, 4.0));
        shapes.add(new Triangle(5.0, 3.0));
        
        System.out.println("=== Double Dispatch mit Visitor Pattern ===");
        
        // Area Visitor
        System.out.println("Area calculation:");
        AreaVisitor areaVisitor = new AreaVisitor();
        for (Shape shape : shapes) {
            shape.accept(areaVisitor); // Double Dispatch!
        }
        System.out.println("Total area: " + String.format("%.2f", areaVisitor.getResult()));
        System.out.println();
        
        // Perimeter Visitor
        System.out.println("Perimeter calculation:");
        PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
        for (Shape shape : shapes) {
            shape.accept(perimeterVisitor); // Double Dispatch!
        }
        System.out.println("Total perimeter: " + String.format("%.2f", perimeterVisitor.getResult()));
        System.out.println();
        
        // XML Export Visitor
        System.out.println("XML Export:");
        XMLExportVisitor xmlVisitor = new XMLExportVisitor();
        for (Shape shape : shapes) {
            shape.accept(xmlVisitor); // Double Dispatch!
        }
        System.out.println(xmlVisitor.getXML());
    }
}

Multiple Dispatch

Simulation of Multiple Dispatch

public class MultipleDispatchDemo {
    
    // Problem: Operations that depend on two types
    public interface Collider {
        void collideWith(Collider other);
        String getType();
    }
    
    public static class Asteroid implements Collider {
        private String name;
        private double mass;
        
        public Asteroid(String name, double mass) {
            this.name = name;
            this.mass = mass;
        }
        
        @Override
        public void collideWith(Collider other) {
            // Single Dispatch - only the other type matters
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "Asteroid";
        }
        
        public double getMass() { return mass; }
        public String getName() { return name; }
    }
    
    public static class Spaceship implements Collider {
        private String name;
        private double shield;
        
        public Spaceship(String name, double shield) {
            this.name = name;
            this.shield = shield;
        }
        
        @Override
        public void collideWith(Collider other) {
            // Single Dispatch - Problem: we don't know which type we have
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "Spaceship";
        }
        
        public double getShield() { return shield; }
        public String getName() { return name; }
    }
    
    public static class SpaceStation implements Collider {
        private String name;
        private double hull;
        
        public SpaceStation(String name, double hull) {
            this.name = name;
            this.hull = hull;
        }
        
        @Override
        public void collideWith(Collider other) {
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "SpaceStation";
        }
        
        public double getHull() { return hull; }
        public String getName() { return name; }
    }
    
    // Solution with Double Dispatch Pattern
    public interface CollisionHandler {
        void handleAsteroidAsteroid(Asteroid a1, Asteroid a2);
        void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship);
        void handleAsteroidStation(Asteroid asteroid, SpaceStation station);
        void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2);
        void handleSpaceshipStation(Spaceship spaceship, SpaceStation station);
        void handleStationStation(SpaceStation s1, SpaceStation s2);
    }
    
    public static class DefaultCollisionHandler implements CollisionHandler {
        @Override
        public void handleAsteroidAsteroid(Asteroid a1, Asteroid a2) {
            double totalMass = a1.getMass() + a2.getMass();
            System.out.println("Asteroid collision: " + a1.getName() + " + " + a2.getName() + 
                             " (total mass: " + String.format("%.1f", totalMass) + ")");
        }
        
        @Override
        public void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship) {
            double damage = asteroid.getMass() * 0.1;
            spaceship.setShield(spaceship.getShield() - damage);
            System.out.println("Asteroid-Spaceship collision: " + asteroid.getName() + 
                             " hits " + spaceship.getName() + " (damage: " + 
                             String.format("%.1f", damage) + ")");
        }
        
        @Override
        public void handleAsteroidStation(Asteroid asteroid, SpaceStation station) {
            double damage = asteroid.getMass() * 0.05;
            station.setHull(station.getHull() - damage);
            System.out.println("Asteroid-Station collision: " + asteroid.getName() + 
                             " hits " + station.getName() + " (damage: " + 
                             String.format("%.1f", damage) + ")");
        }
        
        @Override
        public void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2) {
            System.out.println("Spaceship collision: " + s1.getName() + " + " + s2.getName());
        }
        
        @Override
        public void handleSpaceshipStation(Spaceship spaceship, SpaceStation station) {
            System.out.println("Spaceship-Station collision: " + spaceship.getName() + 
                             " docks at " + station.getName());
        }
        
        @Override
        public void handleStationStation(SpaceStation s1, SpaceStation s2) {
            System.out.println("Station collision: " + s1.getName() + " + " + s2.getName());
        }
    }
    
    // Improved Collider with Double Dispatch
    public static class ImprovedAsteroid extends Asteroid {
        public ImprovedAsteroid(String name, double mass) {
            super(name, mass);
        }
        
        @Override
        public void collideWith(Collider other) {
            other.collideWithAsteroid(this); // Reversal for Double Dispatch
        }
        
        public void collideWithAsteroid(Asteroid other) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidAsteroid(this, other);
        }
        
        public void collideWithSpaceship(Spaceship spaceship) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidSpaceship(this, spaceship);
        }
        
        public void collideWithStation(SpaceStation station) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidStation(this, station);
        }
    }
    
    public static class ImprovedSpaceship extends Spaceship {
        public ImprovedSpaceship(String name, double shield) {
            super(name, shield);
        }
        
        @Override
        public void collideWith(Collider other) {
            other.collideWithSpaceship(this);
        }
        
        public void collideWithAsteroid(Asteroid asteroid) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidSpaceship(asteroid, this);
        }
        
        public void collideWithSpaceship(Spaceship other) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleSpaceshipSpaceship(this, other);
        }
        
        public void collideWithStation(SpaceStation station) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleSpaceshipStation(this, station);
        }
    }
    
    // Demonstration
    public static void demonstrateMultipleDispatch() {
        System.out.println("=== Multiple Dispatch Simulation ===");
        
        // Simple Single Dispatch (Problem)
        System.out.println("Single Dispatch (Problem):");
        Collider asteroid1 = new Asteroid("Rocky", 100.0);
        Collider spaceship1 = new Spaceship("Explorer", 50.0);
        asteroid1.collideWith(spaceship1); // Only type of spaceship1 known
        
        System.out.println();
        
        // Double Dispatch Solution
        System.out.println("Double Dispatch Solution:");
        ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
        ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
        asteroid2.collideWith(spaceship2); // Both types known!
        
        System.out.println();
        
        // More complex collisions
        List<Collider> objects = Arrays.asList(
            new ImprovedAsteroid("Rocky", 100.0),
            new ImprovedSpaceship("Explorer", 50.0),
            new ImprovedSpaceship("Voyager", 60.0),
            new SpaceStation("Alpha", 200.0)
        );
        
        System.out.println("Complex collisions:");
        for (int i = 0; i < objects.size(); i++) {
            for (int j = i + 1; j < objects.size(); j++) {
                objects.get(i).collideWith(objects.get(j));
            }
        }
    }
}

Virtual Method Tables (vtable)

How vtables work

public class VTableDemo {
    
    // Simplified representation of a vtable
    public static class VTableVisualization {
        
        // Base class
        public static class Base {
            // Virtual methods
            public virtual void method1() { System.out.println("Base.method1"); }
            public virtual void method2() { System.out.println("Base.method2"); }
            
            // Concrete method
            public void concreteMethod() { System.out.println("Base.concreteMethod"); }
        }
        
        // Derived class 1
        public static class Derived1 extends Base {
            @Override
            public void method1() { System.out.println("Derived1.method1"); }
            // method2 inherited
            
            @Override
            public void method2() { System.out.println("Derived1.method2"); }
        }
        
        // Derived class 2
        public static class Derived2 extends Base {
            // method1 inherited
            @Override
            public void method2() { System.out.println("Derived2.method2"); }
            
            public void method3() { System.out.println("Derived2.method3"); }
        }
        
        // Demonstration of vtable
        public static void demonstrateVTable() {
            System.out.println("=== VTable Demonstration ===");
            
            Base base = new Base();
            Base derived1 = new Derived1();
            Base derived2 = new Derived2();
            
            // Vtable for Base:
            // [Base.method1, Base.method2]
            System.out.println("Base object:");
            base.method1(); // Base.method1
            base.method2(); // Base.method2
            
            // Vtable for Derived1:
            // [Derived1.method1, Derived1.method2]
            System.out.println("\nDerived1 object:");
            derived1.method1(); // Derived1.method1
            derived1.method2(); // Derived1.method2
            
            // Vtable for Derived2:
            // [Base.method1, Derived2.method2]
            System.out.println("\nDerived2 object:");
            derived2.method1(); // Base.method1 (inherited)
            derived2.method2(); // Derived2.method2
            
            // Concrete method - not in vtable
            System.out.println("\nConcrete method:");
            base.concreteMethod();
            derived1.concreteMethod();
            derived2.concreteMethod();
        }
    }
    
    // Performance comparison
    public static class PerformanceComparison {
        
        // Final method - no virtual call
        public static class FinalClass {
            public final void finalMethod() {
                // No vtable lookup
            }
        }
        
        // Virtual method - vtable lookup
        public static class VirtualClass {
            public void virtualMethod() {
                // vtable lookup required
            }
        }
        
        public static void performanceTest() {
            FinalClass finalObj = new FinalClass();
            VirtualClass virtualObj = new VirtualClass();
            
            int iterations = 100_000_000;
            
            // Test final method
            long start = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                finalObj.finalMethod();
            }
            long finalTime = System.nanoTime() - start;
            
            // Test virtual method
            start = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                virtualObj.virtualMethod();
            }
            long virtualTime = System.nanoTime() - start;
            
            System.out.println("Performance Comparison:");
            System.out.println("Final method: " + (finalTime / 1_000_000) + " ms");
            System.out.println("Virtual method: " + (virtualTime / 1_000_000) + " ms");
            System.out.println("Difference: " + ((virtualTime - finalTime) / 1_000_000) + " ms");
        }
    }
}

Dispatch in Practice

Framework Example: Plugin System

public class PluginDispatchDemo {
    
    // Plugin Interface
    public interface Plugin {
        void initialize();
        void execute();
        void shutdown();
        String getName();
        String getVersion();
    }
    
    // Abstract base class for plugins
    public abstract class AbstractPlugin implements Plugin {
        protected String name;
        protected String version;
        
        public AbstractPlugin(String name, String version) {
            this.name = name;
            this.version = version;
        }
        
        @Override
        public final String getName() {
            return name;
        }
        
        @Override
        public final String getVersion() {
            return version;
        }
        
        // Template Method Pattern
        @Override
        public final void execute() {
            System.out.println("Executing plugin: " + name);
            try {
                doExecute();
                System.out.println("Plugin execution completed: " + name);
            } catch (Exception e) {
                System.err.println("Plugin execution failed: " + name + " - " + e.getMessage());
            }
        }
        
        // Abstract method for concrete implementation
        protected abstract void doExecute();
        
        // Default implementations
        @Override
        public void initialize() {
            System.out.println("Initializing plugin: " + name);
        }
        
        @Override
        public void shutdown() {
            System.out.println("Shutting down plugin: " + name);
        }
    }
    
    // Concrete plugins
    public static class DatabasePlugin extends AbstractPlugin {
        public DatabasePlugin() {
            super("DatabasePlugin", "1.0.0");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Connecting to database...");
            System.out.println("  Executing database operations...");
            System.out.println("  Closing database connection...");
        }
        
        @Override
        public void initialize() {
            super.initialize();
            System.out.println("  Loading database driver...");
        }
    }
    
    public static class LoggingPlugin extends AbstractPlugin {
        public LoggingPlugin() {
            super("LoggingPlugin", "2.1.0");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Setting up loggers...");
            System.out.println("  Configuring log levels...");
            System.out.println("  Starting log rotation...");
        }
        
        @Override
        public void shutdown() {
            System.out.println("  Flushing log buffers...");
            super.shutdown();
        }
    }
    
    public static class SecurityPlugin extends AbstractPlugin {
        public SecurityPlugin() {
            super("SecurityPlugin", "1.5.2");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Initializing security manager...");
            System.out.println("  Setting up authentication...");
            System.out.println("  Configuring authorization rules...");
        }
    }
    
    // Plugin Manager with dispatch
    public static class PluginManager {
        private List<Plugin> plugins = new ArrayList<>();
        
        public void registerPlugin(Plugin plugin) {
            plugins.add(plugin);
            plugin.initialize();
        }
        
        public void executeAllPlugins() {
            System.out.println("=== Executing all plugins ===");
            for (Plugin plugin : plugins) {
                plugin.execute(); // Dynamic dispatch
            }
        }
        
        public void shutdownAllPlugins() {
            System.out.println("\n=== Shutting down all plugins ===");
            for (Plugin plugin : plugins) {
                plugin.shutdown(); // Dynamic dispatch
            }
        }
        
        public void executePluginByName(String name) {
            for (Plugin plugin : plugins) {
                if (plugin.getName().equals(name)) {
                    plugin.execute(); // Dynamic dispatch
                    return;
                }
            }
            System.out.println("Plugin not found: " + name);
        }
        
        public void listPlugins() {
            System.out.println("=== Registered Plugins ===");
            for (Plugin plugin : plugins) {
                System.out.println(plugin.getName() + " v" + plugin.getVersion());
            }
        }
    }
    
    // Demonstration
    public static void demonstratePluginSystem() {
        PluginManager manager = new PluginManager();
        
        // Register plugins
        manager.registerPlugin(new DatabasePlugin());
        manager.registerPlugin(new LoggingPlugin());
        manager.registerPlugin(new SecurityPlugin());
        
        // Execute all plugins
        manager.listPlugins();
        manager.executeAllPlugins();
        
        // Execute specific plugin
        System.out.println("\n=== Executing specific plugin ===");
        manager.executePluginByName("LoggingPlugin");
        
        // Clean up
        manager.shutdownAllPlugins();
    }
}

Best Practices for Dispatch

1. Final for Performance

public class DispatchBestPractices {
    
    // Finale Methoden für Performance
    public static class PerformanceOptimized {
        // Finale Methode - kein virtueller Aufruf
        public final void criticalMethod() {
            // Hot Path Code
        }
        
        // Finale Klasse - keine Vererbung möglich
        public static final class ImmutableData {
            private final int value;
            
            public ImmutableData(int value) {
                this.value = value;
            }
            
            public final int getValue() {
                return value;
            }
        }
    }
    
    // Interface für Flexibilität
    public interface Processor {
        void process();
    }
    
    // Konkrete Implementierung
    public static class FastProcessor implements Processor {
        @Override
        public void process() {
            // Implementierung
        }
    }
}

2. Avoiding Dispatch Problems

public class DispatchProblems {
    
    // Problem: Überladung mit Referenztypen
    public static class ProblematicOverloading {
        public void process(Object obj) {
            System.out.println("Processing Object");
        }
        
        public void process(String str) {
            System.out.println("Processing String");
        }
        
        public void demonstrateProblem() {
            Object obj = "Hello";
            process(obj); // Ruft process(Object) auf!
        }
    }
    
    // Lösung: Klare Methodennamen
    public static class Solution {
        public void processObject(Object obj) {
            System.out.println("Processing Object");
        }
        
        public void processString(String str) {
            System.out.println("Processing String");
        }
        
        public void demonstrateSolution() {
            Object obj = "Hello";
            if (obj instanceof String) {
                processString((String) obj);
            } else {
                processObject(obj);
            }
        }
    }
}

Exam-Relevant Concepts

Important Distinctions

  1. Static vs Dynamic

    • Static: Compile-time (Overloading)
    • Dynamic: Runtime (Overriding)
  2. Single vs Double Dispatch

    • Single: One object type decides
    • Double: Two object types decide
  3. vtable vs Early Binding

    • vtable: Virtual methods
    • Early Binding: Final methods

Typical Exam Tasks

  1. Explain Single vs Double Dispatch
  2. Implement Visitor Pattern
  3. Compare static and dynamic binding
  4. Describe vtable functionality
  5. Solve dispatch problems

Summary

Dispatch is fundamental to object-oriented programming:

  • Static Dispatch: Predictable, performant (Overloading)
  • Dynamic Dispatch: Flexible, extensible (Overriding)
  • Single Dispatch: Standard in most OOP languages
  • Double Dispatch: Realizable via Visitor Pattern
  • vtables: Efficient implementation of dynamic dispatch

The correct application of dispatch enables flexible, maintainable, and performant software architectures.

Back to Blog
Share:

Related Posts