Skip to content
IRC-Coding IRC-Coding
OOP Dispatch Dynamische Bindung Single Dispatch Double Dispatch Visitor Pattern Mehrfachdispatch vtable Polymorphie

OOP Dispatch: Dynamische Bindung, Single & Double Dispatch

OOP Dispatch mit dynamischer Bindung, Single/Double Dispatch und Visitor Pattern. Java Beispiele für Methodenauflösung und Mehrfachdispatch.

S

schutzgeist

2 min read

OOP Dispatch: Dynamische Bindung, Single & Double Dispatch

Dispatch bezeichnet die Auswahl der tatsächlich auszuführenden Methode bei einem Methodenaufruf. Diese Auswahl kann statisch zur Kompilierzeit oder dynamisch zur Laufzeit erfolgen und ist die Grundlage für Polymorphie.

Was ist Dispatch?

Dispatch ist der Mechanismus, der einen Methodenaufruf auf eine konkrete Implementierung auflöst. Die Art der Dispatch-Auflösung beeinflusst die Flexibilität und Performance der Software.

Arten des Dispatch

  1. Statischer Dispatch: Entscheidung zur Kompilierzeit
  2. Dynamischer Dispatch: Entscheidung zur Laufzeit
  3. Single Dispatch: Auswahl basierend auf Empfängerobjekt
  4. Double Dispatch: Auswahl basierend auf zwei Objekttypen
  5. Mehrfachdispatch: Auswahl basierend auf mehreren Objekttypen

Statischer vs Dynamischer Dispatch

Statischer Dispatch (Überladen)

public class StaticDispatchDemo {
    
    // Überladene Methoden - statischer 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();
        
        // Direkte Aufrufe - eindeutige Auswahl
        printer.print("Hello");           // print(String)
        printer.print(42);               // print(Integer)
        printer.print("Test", 3);         // print(String, int)
        
        // Wichtiger Fall: Statische Typen entscheiden!
        Object obj1 = "Hello";
        Object obj2 = 42;
        
        printer.print(obj1);  // print(Object) - Compiler kennt nur Object-Typ!
        printer.print(obj2);  // print(Object) - Compiler kennt nur Object-Typ!
        
        // Cast zur Auswahl der richtigen Überladung
        printer.print((String) obj1);   // print(String)
        // printer.print((Integer) obj2); // ClassCastException zur Laufzeit!
    }
}

Dynamischer Dispatch (Überschreiben)

public class DynamicDispatchDemo {
    
    // Basisklasse mit virtuellen Methoden
    public static abstract class Animal {
        protected String name;
        
        public Animal(String name) {
            this.name = name;
        }
        
        // Virtuelle Methode - wird dynamisch gebunden
        public String makeSound() {
            return name + " makes a sound";
        }
        
        // Weitere virtuelle Methode
        public String move() {
            return name + " moves";
        }
        
        // Finale Methode - kann nicht überschrieben werden
        public final String getName() {
            return name;
        }
        
        // Abstrakte Methode - muss überschrieben werden
        public abstract String getType();
    }
    
    // Konkrete Unterklassen
    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";
        }
        
        // Zusätzliche Methode
        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 von dynamischem 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("=== Dynamischer Dispatch ===");
        for (Animal animal : animals) {
            // Dynamische Bindung - Laufzeit entscheidet!
            System.out.println(animal.getType() + ": " + animal.makeSound());
            System.out.println(animal.getType() + ": " + animal.move());
            System.out.println();
        }
        
        // Polymorphe Verarbeitung
        processAnimal(animals.get(0)); // Dog
        processAnimal(animals.get(1)); // Cat
        processAnimal(animals.get(2)); // Bird
    }
    
    // Polymorphe Methode
    public static void processAnimal(Animal animal) {
        System.out.println("Processing " + animal.getType());
        
        // Dynamischer Dispatch
        String sound = animal.makeSound();
        String movement = animal.move();
        
        System.out.println("  Sound: " + sound);
        System.out.println("  Movement: " + movement);
        
        // Type-spezifische Operationen (mit 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

Wie Single Dispatch funktioniert

public class SingleDispatchDemo {
    
    // Single Dispatch - nur der Empfängertyp entscheidet
    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() {
            // Vereinfacht: gleichseitiges Dreieck
            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 mit Visitor Pattern

Visitor Pattern für 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("Flächenberechnung:");
        AreaVisitor areaVisitor = new AreaVisitor();
        for (Shape shape : shapes) {
            shape.accept(areaVisitor); // Double Dispatch!
        }
        System.out.println("Gesamtfläche: " + String.format("%.2f", areaVisitor.getResult()));
        System.out.println();
        
        // Perimeter Visitor
        System.out.println("Umfangberechnung:");
        PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
        for (Shape shape : shapes) {
            shape.accept(perimeterVisitor); // Double Dispatch!
        }
        System.out.println("Gesamtumfang: " + 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());
    }
}

Mehrfachdispatch (Multiple Dispatch)

Simulation von Mehrfachdispatch

public class MultipleDispatchDemo {
    
    // Problem: Operationen, die von zwei Typen abhängen
    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 - nur anderer Typ entscheidet
            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: wir wissen nicht welchen Typ wir haben
            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; }
    }
    
    // Lösung mit 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());
        }
    }
    
    // Verbesserte Collider mit 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); // Umkehrung für 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 ===");
        
        // Einfacher 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); // Nur Typ von spaceship1 bekannt
        
        System.out.println();
        
        // Double Dispatch Lösung
        System.out.println("Double Dispatch Solution:");
        ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
        ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
        asteroid2.collideWith(spaceship2); // Beide Typen bekannt!
        
        System.out.println();
        
        // Komplexere Kollisionen
        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));
            }
        }
    }
}

Virtuelle Methodentabellen (vtable)

Wie vtables funktionieren

public class VTableDemo {
    
    // Vereinfachte Darstellung einer vtable
    public static class VTableVisualization {
        
        // Basisklasse
        public static class Base {
            // Virtuelle Methoden
            public virtual void method1() { System.out.println("Base.method1"); }
            public virtual void method2() { System.out.println("Base.method2"); }
            
            // Konkrete Methode
            public void concreteMethod() { System.out.println("Base.concreteMethod"); }
        }
        
        // Abgeleitete Klasse 1
        public static class Derived1 extends Base {
            @Override
            public void method1() { System.out.println("Derived1.method1"); }
            // method2 geerbt
            
            @Override
            public void method2() { System.out.println("Derived1.method2"); }
        }
        
        // Abgeleitete Klasse 2
        public static class Derived2 extends Base {
            // method1 geerbt
            @Override
            public void method2() { System.out.println("Derived2.method2"); }
            
            public void method3() { System.out.println("Derived2.method3"); }
        }
        
        // Demonstration der vtable
        public static void demonstrateVTable() {
            System.out.println("=== VTable Demonstration ===");
            
            Base base = new Base();
            Base derived1 = new Derived1();
            Base derived2 = new Derived2();
            
            // Vtable für Base:
            // [Base.method1, Base.method2]
            System.out.println("Base object:");
            base.method1(); // Base.method1
            base.method2(); // Base.method2
            
            // Vtable für Derived1:
            // [Derived1.method1, Derived1.method2]
            System.out.println("\nDerived1 object:");
            derived1.method1(); // Derived1.method1
            derived1.method2(); // Derived1.method2
            
            // Vtable für Derived2:
            // [Base.method1, Derived2.method2]
            System.out.println("\nDerived2 object:");
            derived2.method1(); // Base.method1 (geerbt)
            derived2.method2(); // Derived2.method2
            
            // Konkrete Methode - nicht in vtable
            System.out.println("\nConcrete method:");
            base.concreteMethod();
            derived1.concreteMethod();
            derived2.concreteMethod();
        }
    }
    
    // Performance-Vergleich
    public static class PerformanceComparison {
        
        // Finale Methode - kein virtueller Aufruf
        public static class FinalClass {
            public final void finalMethod() {
                // Kein vtable Lookup
            }
        }
        
        // Virtuelle Methode - vtable Lookup
        public static class VirtualClass {
            public void virtualMethod() {
                // vtable Lookup erforderlich
            }
        }
        
        public static void performanceTest() {
            FinalClass finalObj = new FinalClass();
            VirtualClass virtualObj = new VirtualClass();
            
            int iterations = 100_000_000;
            
            // Test finale Methode
            long start = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                finalObj.finalMethod();
            }
            long finalTime = System.nanoTime() - start;
            
            // Test virtuelle Methode
            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 der Praxis

Framework-Beispiel: Plugin-System

public class PluginDispatchDemo {
    
    // Plugin Interface
    public interface Plugin {
        void initialize();
        void execute();
        void shutdown();
        String getName();
        String getVersion();
    }
    
    // Abstrakte Basisklasse für 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());
            }
        }
        
        // Abstrakte Methode für konkrete Implementierung
        protected abstract void doExecute();
        
        // Standardimplementierungen
        @Override
        public void initialize() {
            System.out.println("Initializing plugin: " + name);
        }
        
        @Override
        public void shutdown() {
            System.out.println("Shutting down plugin: " + name);
        }
    }
    
    // Konkrete 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 mit 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(); // Dynamischer Dispatch
            }
        }
        
        public void shutdownAllPlugins() {
            System.out.println("\n=== Shutting down all plugins ===");
            for (Plugin plugin : plugins) {
                plugin.shutdown(); // Dynamischer Dispatch
            }
        }
        
        public void executePluginByName(String name) {
            for (Plugin plugin : plugins) {
                if (plugin.getName().equals(name)) {
                    plugin.execute(); // Dynamischer 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();
        
        // Plugins registrieren
        manager.registerPlugin(new DatabasePlugin());
        manager.registerPlugin(new LoggingPlugin());
        manager.registerPlugin(new SecurityPlugin());
        
        // Alle Plugins ausführen
        manager.listPlugins();
        manager.executeAllPlugins();
        
        // Spezifisches Plugin ausführen
        System.out.println("\n=== Executing specific plugin ===");
        manager.executePluginByName("LoggingPlugin");
        
        // Aufräumen
        manager.shutdownAllPlugins();
    }
}

Best Practices für Dispatch

1. Final für 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. Vermeidung von Dispatch-Problemen

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);
            }
        }
    }
}

Prüfungsrelevante Konzepte

Wichtige Unterscheidungen

  1. Statisch vs Dynamisch

    • Statisch: Compiler-zeit (Überladen)
    • Dynamisch: Laufzeit (Überschreiben)
  2. Single vs Double Dispatch

    • Single: Ein Objekttyp entscheidet
    • Double: Zwei Objekttypen entscheiden
  3. vtable vs Early Binding

    • vtable: Virtuelle Methoden
    • Early Binding: Finale Methoden

Typische Prüfungsaufgaben

  1. Erklären Sie Single vs Double Dispatch
  2. Implementieren Sie Visitor Pattern
  3. Vergleichen Sie statische und dynamische Bindung
  4. Beschreiben Sie vtable Funktionsweise
  5. Lösen Sie Dispatch-Probleme

Zusammenfassung

Dispatch ist fundamental für objektorientierte Programmierung:

  • Statischer Dispatch: Vorhersagbar, performant (Überladen)
  • Dynamischer Dispatch: Flexibel, erweiterbar (Überschreiben)
  • Single Dispatch: Standard in den meisten OOP-Sprachen
  • Double Dispatch: Über Visitor Pattern realisierbar
  • vtables: Effiziente Implementierung dynamischen Dispatch

Die richtige Anwendung von Dispatch ermöglicht flexible, wartbare und performante Softwarearchitekturen.

Zurück zum Blog
Share:

Ähnliche Beiträge